master: complete hooking up tokens and IPKI

This makes a few changes necessary to complete hooking up the master
with the IPKI system and tokens:

* When a master first becomes leader, it uses the CA cert to sign its
  own server cert, and then adopts it. To make this code a little less
  messy, I made AddTrustedCertificate idempotent rather than returning
  AlreadyPresent() if the cert was already trusted.

* The TokenSigner's associated TokenVerifier is now passed in as a
  constructor argument rather than constructed as part of the signer.
  This allows the Signer to be hooked up to the same Verifier used by
  the Messenger to verify incoming connections using TOKEN
  authentication.

  I took the most expedient path here of using shared ownership, since
  the Messenger lifecycle is quite tricky and I wasn't completely sure
  that the teardown sequence would be safe with single-ownership. The
  chances of shared_ptr cycles should be low since TokenVerifier is a
  standalone class.

This also adds a new simple test which verifies that, when starting a
master, it learns about a TSK and signs its own cert.

Change-Id: I3dfadb427491c7b406ad2d2bc1245b3a1cdb9170
Reviewed-on: http://gerrit.cloudera.org:8080/6075
Reviewed-by: Dan Burkert <[email protected]>
Reviewed-by: Alexey Serbin <[email protected]>
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/dc454dcd
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/dc454dcd
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/dc454dcd

Branch: refs/heads/master
Commit: dc454dcd082d0ca879aef911391751254d96c3f2
Parents: 3102e38
Author: Todd Lipcon <[email protected]>
Authored: Sat Feb 18 18:38:39 2017 -0800
Committer: Todd Lipcon <[email protected]>
Committed: Wed Feb 22 05:53:26 2017 +0000

----------------------------------------------------------------------
 src/kudu/client/client-internal.cc              |  4 +---
 src/kudu/integration-tests/delete_table-test.cc | 11 +++++++++--
 .../integration-tests/master_failover-itest.cc  |  2 +-
 .../integration-tests/token_signer-itest.cc     |  2 +-
 src/kudu/master/catalog_manager.cc              | 20 +++++++++++++++++++-
 src/kudu/master/master-test.cc                  | 11 +++++++++++
 src/kudu/master/master.cc                       |  3 ++-
 src/kudu/master/master_cert_authority.cc        | 17 ++++++++++++-----
 src/kudu/master/master_cert_authority.h         | 10 ++++++++++
 src/kudu/rpc/messenger.h                        |  5 ++++-
 src/kudu/rpc/negotiation-test.cc                |  2 +-
 src/kudu/security/tls_context.cc                |  6 +++---
 src/kudu/security/tls_context.h                 |  3 +--
 src/kudu/security/token-test.cc                 | 18 ++++++++++--------
 src/kudu/security/token_signer.cc               |  7 +++++--
 src/kudu/security/token_signer.h                |  8 ++++++--
 16 files changed, 96 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/client/client-internal.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/client-internal.cc 
b/src/kudu/client/client-internal.cc
index 0b64cf2..9a2d81d 100644
--- a/src/kudu/client/client-internal.cc
+++ b/src/kudu/client/client-internal.cc
@@ -607,9 +607,7 @@ void KuduClient::Data::ConnectedToClusterCb(
         continue;
       }
       s = messenger_->mutable_tls_context()->AddTrustedCertificate(cert);
-      // In the case that we are just re-connecting, then the attempt to trust 
the cert
-      // will return AlreadyPresent, since we already have the cert trusted.
-      if (!s.ok() && !s.IsAlreadyPresent()) {
+      if (!s.ok()) {
         KLOG_EVERY_N_SECS(WARNING, 5) << "Master " << leader_addr.ToString()
                                      << " provided a cert that could not be 
trusted: "
                                      << s.ToString();

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/integration-tests/delete_table-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/integration-tests/delete_table-test.cc 
b/src/kudu/integration-tests/delete_table-test.cc
index abe52c7..08a4554 100644
--- a/src/kudu/integration-tests/delete_table-test.cc
+++ b/src/kudu/integration-tests/delete_table-test.cc
@@ -1080,10 +1080,17 @@ TEST_F(DeleteTableTest, 
TestUnknownTabletsAreNotDeleted) {
 
   // Now restart the master with orphan deletion enabled. The tablet should get
   // deleted.
-  cluster_->master()->Shutdown();
+  // We also need to restart the tablet server, or else the old tablet server
+  // won't be able to authenticate to the new master, due to it having a new
+  // CA certificate which the old tserver doesn't trust.
+  //
+  // TODO(PKI): perhaps this is actually a feature? should we have tablet 
servers
+  // remember the CA cert persistently so that it's impossible to connect an
+  // old tserver to a new cluster?
+  cluster_->Shutdown();
   cluster_->master()->mutable_flags()->push_back(
       "--catalog_manager_delete_orphaned_tablets");
-  ASSERT_OK(cluster_->master()->Restart());
+  ASSERT_OK(cluster_->Restart());
   AssertEventually([&]() {
     ASSERT_OK(cluster_->tablet_server(0)->GetInt64Metric(
         &METRIC_ENTITY_server, "kudu.tabletserver",

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/integration-tests/master_failover-itest.cc
----------------------------------------------------------------------
diff --git a/src/kudu/integration-tests/master_failover-itest.cc 
b/src/kudu/integration-tests/master_failover-itest.cc
index 3534978..5288e89 100644
--- a/src/kudu/integration-tests/master_failover-itest.cc
+++ b/src/kudu/integration-tests/master_failover-itest.cc
@@ -457,7 +457,7 @@ TEST_F(MasterFailoverTest, TestMasterPermanentFailure) {
         // See TestCreateTableSync to understand why we must check for
         // IsAlreadyPresent as well.
         Status s = CreateTable(table_name, kWaitForCreate);
-        ASSERT_TRUE(s.ok() || s.IsAlreadyPresent());
+        ASSERT_TRUE(s.ok() || s.IsAlreadyPresent()) << s.ToString();
 
         ASSERT_OK(client_->OpenTable(table_name, &table));
         ASSERT_EQ(0, CountTableRows(table.get()));

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/integration-tests/token_signer-itest.cc
----------------------------------------------------------------------
diff --git a/src/kudu/integration-tests/token_signer-itest.cc 
b/src/kudu/integration-tests/token_signer-itest.cc
index 121f4d1..823169c 100644
--- a/src/kudu/integration-tests/token_signer-itest.cc
+++ b/src/kudu/integration-tests/token_signer-itest.cc
@@ -111,7 +111,7 @@ class TokenSignerITest : public KuduTest {
     CHECK_LT(idx, num_masters_);
     MiniMaster* mm = cluster_->mini_master(idx);
     vector<TokenSigningPublicKeyPB> keys =
-        mm->master()->token_signer()->verifier().ExportKeys();
+        mm->master()->token_verifier().ExportKeys();
     public_keys->swap(keys);
     return Status::OK();
   }

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/master/catalog_manager.cc
----------------------------------------------------------------------
diff --git a/src/kudu/master/catalog_manager.cc 
b/src/kudu/master/catalog_manager.cc
index c5456ca..9140b52 100644
--- a/src/kudu/master/catalog_manager.cc
+++ b/src/kudu/master/catalog_manager.cc
@@ -83,6 +83,7 @@
 #include "kudu/rpc/rpc_context.h"
 #include "kudu/security/cert.h"
 #include "kudu/security/crypto.h"
+#include "kudu/security/tls_context.h"
 #include "kudu/security/token.pb.h"
 #include "kudu/security/token_signer.h"
 #include "kudu/security/token_signing_key.h"
@@ -759,7 +760,24 @@ Status 
CatalogManager::LoadCertAuthorityInfo(unique_ptr<PrivateKey>* key,
 Status CatalogManager::InitCertAuthority(unique_ptr<PrivateKey> key,
                                          unique_ptr<Cert> cert) {
   leader_lock_.AssertAcquiredForWriting();
-  return master_->cert_authority()->Init(std::move(key), std::move(cert));
+  auto* ca = master_->cert_authority();
+  RETURN_NOT_OK_PREPEND(ca->Init(std::move(key), std::move(cert)),
+                        "could not init master CA");
+
+  auto* tls = master_->mutable_tls_context();
+  RETURN_NOT_OK_PREPEND(tls->AddTrustedCertificate(ca->ca_cert()),
+                        "could not trust master CA cert");
+  // If we haven't signed our own server cert yet, do so.
+  boost::optional<security::CertSignRequest> csr =
+      tls->GetCsrIfNecessary();
+  if (csr) {
+    Cert cert;
+    RETURN_NOT_OK_PREPEND(ca->SignServerCSR(*csr, &cert),
+                          "couldn't sign master cert with CA cert");
+    RETURN_NOT_OK_PREPEND(tls->AdoptSignedCert(cert),
+                          "couldn't adopt signed master cert");
+  }
+  return Status::OK();
 }
 
 // Store internal Kudu CA cert authority information into the system table.

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/master/master-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/master/master-test.cc b/src/kudu/master/master-test.cc
index 020c4d4..71333d2 100644
--- a/src/kudu/master/master-test.cc
+++ b/src/kudu/master/master-test.cc
@@ -36,6 +36,8 @@
 #include "kudu/master/ts_descriptor.h"
 #include "kudu/master/ts_manager.h"
 #include "kudu/rpc/messenger.h"
+#include "kudu/security/tls_context.h"
+#include "kudu/security/token_verifier.h"
 #include "kudu/server/rpc_server.h"
 #include "kudu/util/curl_util.h"
 #include "kudu/util/pb_util.h"
@@ -1333,5 +1335,14 @@ TEST_F(MasterTest, TestConnectToMaster) {
   ASSERT_TRUE(token.authn().has_username());
 }
 
+// Test that the master signs its on server certificate when it becomes the 
leader,
+// and also that it loads TSKs into the messenger's verifier.
+TEST_F(MasterTest, TestSignOwnCertAndLoadTSKs) {
+  AssertEventually([&]() {
+      ASSERT_TRUE(master_->tls_context().has_signed_cert());
+      
ASSERT_GT(master_->messenger()->token_verifier().GetMaxKnownKeySequenceNumber(),
 -1);
+    });
+}
+
 } // namespace master
 } // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/master/master.cc
----------------------------------------------------------------------
diff --git a/src/kudu/master/master.cc b/src/kudu/master/master.cc
index 0f04c22..7c5ed46 100644
--- a/src/kudu/master/master.cc
+++ b/src/kudu/master/master.cc
@@ -120,7 +120,8 @@ Status Master::Init() {
 
   // The TokenSigner loads its keys during catalog manager initialization.
   token_signer_.reset(new TokenSigner(FLAGS_tsk_validity_seconds,
-                                      FLAGS_tsk_rotation_seconds));
+                                      FLAGS_tsk_rotation_seconds,
+                                      messenger_->shared_token_verifier()));
   state_ = kInitialized;
   return Status::OK();
 }

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/master/master_cert_authority.cc
----------------------------------------------------------------------
diff --git a/src/kudu/master/master_cert_authority.cc 
b/src/kudu/master/master_cert_authority.cc
index f4cad57..75d8b73 100644
--- a/src/kudu/master/master_cert_authority.cc
+++ b/src/kudu/master/master_cert_authority.cc
@@ -113,6 +113,17 @@ Status MasterCertAuthority::Init(unique_ptr<PrivateKey> 
key,
   return Status::OK();
 }
 
+Status MasterCertAuthority::SignServerCSR(const CertSignRequest& csr,
+                                          Cert* cert) {
+  CHECK(ca_cert_ && ca_private_key_) << "not initialized";
+  RETURN_NOT_OK_PREPEND(CertSigner(ca_cert_.get(), ca_private_key_.get())
+                        .set_expiration_interval(MonoDelta::FromSeconds(
+                            FLAGS_ipki_server_cert_expiration_seconds))
+                        .Sign(csr, cert),
+                        "failed to sign cert");
+  return Status::OK();
+}
+
 Status MasterCertAuthority::SignServerCSR(const string& csr_der, string* 
cert_der) {
   CHECK(ca_cert_ && ca_private_key_) << "not initialized";
 
@@ -125,11 +136,7 @@ Status MasterCertAuthority::SignServerCSR(const string& 
csr_der, string* cert_de
   RETURN_NOT_OK_PREPEND(csr.FromString(csr_der, security::DataFormat::DER),
                         "could not parse CSR");
   Cert cert;
-  RETURN_NOT_OK_PREPEND(CertSigner(ca_cert_.get(), ca_private_key_.get())
-                        .set_expiration_interval(MonoDelta::FromSeconds(
-                            FLAGS_ipki_server_cert_expiration_seconds))
-                        .Sign(csr, &cert),
-                        "failed to sign cert");
+  RETURN_NOT_OK(SignServerCSR(csr, &cert));
 
   RETURN_NOT_OK_PREPEND(cert.ToString(cert_der, security::DataFormat::DER),
                         "failed to signed cert as DER format");

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/master/master_cert_authority.h
----------------------------------------------------------------------
diff --git a/src/kudu/master/master_cert_authority.h 
b/src/kudu/master/master_cert_authority.h
index 4e299d1..fd7d675 100644
--- a/src/kudu/master/master_cert_authority.h
+++ b/src/kudu/master/master_cert_authority.h
@@ -31,6 +31,7 @@ class Status;
 namespace security {
 
 class Cert;
+class CertSignRequest;
 class PrivateKey;
 
 namespace ca {
@@ -80,6 +81,9 @@ class MasterCertAuthority {
   //        to keep the internal state consistent.
   Status SignServerCSR(const std::string& csr_der, std::string* cert_der);
 
+  // Same as above, but with objects instead of the DER format CSR/cert.
+  Status SignServerCSR(const security::CertSignRequest& csr, security::Cert* 
cert);
+
   // Export the current CA certificate in DER format.
   //
   // This can be sent to participants in the cluster so they can add it to
@@ -88,6 +92,12 @@ class MasterCertAuthority {
     CHECK(ca_cert_) << "must Init()";
     return ca_cert_der_;
   }
+
+  const security::Cert& ca_cert() const {
+    CHECK(ca_cert_) << "must Init()";
+    return *ca_cert_;
+  }
+
  private:
   friend class ::kudu::master::MasterCertAuthorityTest;
   // The UUID of the master. This is used as a field in the certificate.

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/rpc/messenger.h
----------------------------------------------------------------------
diff --git a/src/kudu/rpc/messenger.h b/src/kudu/rpc/messenger.h
index 9bbecec..c232bf5 100644
--- a/src/kudu/rpc/messenger.h
+++ b/src/kudu/rpc/messenger.h
@@ -199,6 +199,9 @@ class Messenger {
 
   const security::TokenVerifier& token_verifier() const { return 
*token_verifier_; }
   security::TokenVerifier* mutable_token_verifier() { return 
token_verifier_.get(); }
+  std::shared_ptr<security::TokenVerifier> shared_token_verifier() const {
+    return token_verifier_;
+  }
 
   boost::optional<security::SignedTokenPB> authn_token() const {
     std::lock_guard<simple_spinlock> l(authn_token_lock_);
@@ -265,7 +268,7 @@ class Messenger {
   std::unique_ptr<security::TlsContext> tls_context_;
 
   // A TokenVerifier, which can verify client provided authentication tokens.
-  std::unique_ptr<security::TokenVerifier> token_verifier_;
+  std::shared_ptr<security::TokenVerifier> token_verifier_;
 
   // An optional token, which can be used to authenticate to a server.
   mutable simple_spinlock authn_token_lock_;

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/rpc/negotiation-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/rpc/negotiation-test.cc b/src/kudu/rpc/negotiation-test.cc
index fac759f..ac8655b 100644
--- a/src/kudu/rpc/negotiation-test.cc
+++ b/src/kudu/rpc/negotiation-test.cc
@@ -162,7 +162,7 @@ TEST_P(TestNegotiation, TestNegotiation) {
   FLAGS_rpc_encrypt_loopback_connections = desc.rpc_encrypt_loopback;
 
   // Generate an optional client token and server token verifier.
-  TokenSigner token_signer(60, 20);
+  TokenSigner token_signer(60, 20, std::make_shared<TokenVerifier>());
   {
     unique_ptr<TokenSigningPrivateKey> key;
     ASSERT_OK(token_signer.CheckNeedKey(&key));

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/security/tls_context.cc
----------------------------------------------------------------------
diff --git a/src/kudu/security/tls_context.cc b/src/kudu/security/tls_context.cc
index 61d9aba..2c248ca 100644
--- a/src/kudu/security/tls_context.cc
+++ b/src/kudu/security/tls_context.cc
@@ -218,13 +218,13 @@ Status TlsContext::AddTrustedCertificate(const Cert& 
cert) {
   auto* cert_store = SSL_CTX_get_cert_store(ctx_.get());
   int rc = X509_STORE_add_cert(cert_store, cert.GetRawData());
   if (rc <= 0) {
-    // Translate the common case of re-adding a cert that is already in the
-    // trust store into an AlreadyPresent status.
+    // Ignore the common case of re-adding a cert that is already in the
+    // trust store.
     auto err = ERR_peek_error();
     if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
         ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
       ERR_clear_error();
-      return Status::AlreadyPresent("certificate already trusted");
+      return Status::OK();
     }
     OPENSSL_RET_NOT_OK(rc, "failed to add trusted certificate");
   }

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/security/tls_context.h
----------------------------------------------------------------------
diff --git a/src/kudu/security/tls_context.h b/src/kudu/security/tls_context.h
index 3e904bc..f26afec 100644
--- a/src/kudu/security/tls_context.h
+++ b/src/kudu/security/tls_context.h
@@ -98,8 +98,7 @@ class TlsContext {
   // any CA certificates that are part of the certificate chain for the cert
   // passed in to 'UseCertificateAndKey()' or 'AdoptSignedCert()'.
   //
-  // Returns AlreadyPresent if the cert is already marked as trusted. Other
-  // OpenSSL errors will be RuntimeError.
+  // If this cert has already been marked as trusted, this has no effect.
   Status AddTrustedCertificate(const Cert& cert);
 
   // Uses 'cert' and 'key' as the cert and key for use with TLS connections.

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/security/token-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/security/token-test.cc b/src/kudu/security/token-test.cc
index fc9937b..5adeb25 100644
--- a/src/kudu/security/token-test.cc
+++ b/src/kudu/security/token-test.cc
@@ -31,6 +31,7 @@
 
 DECLARE_int32(tsk_num_rsa_bits);
 
+using std::make_shared;
 using std::unique_ptr;
 
 namespace kudu {
@@ -83,6 +84,7 @@ Status GenerateTokenSigningKey(int64_t seq_num,
 } // anonymous namespace
 
 class TokenTest : public KuduTest {
+ public:
   void SetUp() override {
     KuduTest::SetUp();
     // Set the keylength smaller to make tests run faster.
@@ -91,7 +93,7 @@ class TokenTest : public KuduTest {
 };
 
 TEST_F(TokenTest, TestInit) {
-  TokenSigner signer(60, 20);
+  TokenSigner signer(60, 20, make_shared<TokenVerifier>());
   const TokenVerifier& verifier(signer.verifier());
 
   SignedTokenPB token = MakeUnsignedToken(WallTime_Now());
@@ -120,7 +122,7 @@ TEST_F(TokenTest, TestInit) {
 
 TEST_F(TokenTest, TestTokenSignerAddKeys) {
   {
-    TokenSigner signer(60, 20);
+    TokenSigner signer(60, 20, make_shared<TokenVerifier>());
     std::unique_ptr<TokenSigningPrivateKey> key;
     ASSERT_OK(signer.CheckNeedKey(&key));
     // No keys are available yet, so should be able to add.
@@ -135,7 +137,7 @@ TEST_F(TokenTest, TestTokenSignerAddKeys) {
   {
     // Special configuration for TokenSigner: rotation interval is zero,
     // so should be able to add two keys right away.
-    TokenSigner signer(60, 0);
+    TokenSigner signer(60, 0, make_shared<TokenVerifier>());
     std::unique_ptr<TokenSigningPrivateKey> key;
     ASSERT_OK(signer.CheckNeedKey(&key));
     // No keys are available yet, so should be able to add.
@@ -156,7 +158,7 @@ TEST_F(TokenTest, TestTokenSignerAddKeys) {
     // Special configuration for TokenSigner: key rotation interval
     // just one second shorter than the validity interval. It should not
     // need next key right away, but should need next key after 1 second.
-    TokenSigner signer(60, 1);
+    TokenSigner signer(60, 1, make_shared<TokenVerifier>());
     std::unique_ptr<TokenSigningPrivateKey> key;
     ASSERT_OK(signer.CheckNeedKey(&key));
     // No keys are available yet, so should be able to add.
@@ -183,7 +185,7 @@ TEST_F(TokenTest, TestTokenSignerAddKeys) {
 // Test how test rotation works.
 TEST_F(TokenTest, TestTokenSignerSignVerifyExport) {
   // Key rotation interval 0 allows adding 2 keys in a row with no delay.
-  TokenSigner signer(60, 0);
+  TokenSigner signer(60, 0, make_shared<TokenVerifier>());
   const TokenVerifier& verifier(signer.verifier());
 
   // Should start off with no signing keys.
@@ -247,7 +249,7 @@ TEST_F(TokenTest, TestExportKeys) {
   // Test that the exported public keys don't contain private key material,
   // and have an appropriate expiration.
   const int64_t key_exp_seconds = 60;
-  TokenSigner signer(key_exp_seconds, 30);
+  TokenSigner signer(key_exp_seconds, 30, make_shared<TokenVerifier>());
   int64_t key_seq_num;
   {
     std::unique_ptr<TokenSigningPrivateKey> key;
@@ -271,7 +273,7 @@ TEST_F(TokenTest, TestExportKeys) {
 // Test that the TokenVerifier can import keys exported by the TokenSigner
 // and then verify tokens signed by it.
 TEST_F(TokenTest, TestEndToEnd_Valid) {
-  TokenSigner signer(60, 20);
+  TokenSigner signer(60, 20, make_shared<TokenVerifier>());
   {
     std::unique_ptr<TokenSigningPrivateKey> key;
     ASSERT_OK(signer.CheckNeedKey(&key));
@@ -294,7 +296,7 @@ TEST_F(TokenTest, TestEndToEnd_Valid) {
 // See VerificationResult.
 TEST_F(TokenTest, TestEndToEnd_InvalidCases) {
   // Key rotation interval 0 allows adding 2 keys in a row with no delay.
-  TokenSigner signer(60, 0);
+  TokenSigner signer(60, 0, make_shared<TokenVerifier>());
   {
     std::unique_ptr<TokenSigningPrivateKey> key;
     ASSERT_OK(signer.CheckNeedKey(&key));

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/security/token_signer.cc
----------------------------------------------------------------------
diff --git a/src/kudu/security/token_signer.cc 
b/src/kudu/security/token_signer.cc
index 2cf85ae..adf6a51 100644
--- a/src/kudu/security/token_signer.cc
+++ b/src/kudu/security/token_signer.cc
@@ -51,6 +51,7 @@ DEFINE_int32(tsk_num_rsa_bits, 2048,
 
 using std::lock_guard;
 using std::map;
+using std::shared_ptr;
 using std::string;
 using std::unique_lock;
 using std::unique_ptr;
@@ -60,11 +61,13 @@ namespace kudu {
 namespace security {
 
 TokenSigner::TokenSigner(int64_t key_validity_seconds,
-                         int64_t key_rotation_seconds)
-    : verifier_(new TokenVerifier),
+                         int64_t key_rotation_seconds,
+                         shared_ptr<TokenVerifier> verifier)
+    : verifier_(std::move(verifier)),
       key_validity_seconds_(key_validity_seconds),
       key_rotation_seconds_(key_rotation_seconds),
       next_key_seq_num_(0) {
+  CHECK(verifier_);
 }
 
 TokenSigner::~TokenSigner() {

http://git-wip-us.apache.org/repos/asf/kudu/blob/dc454dcd/src/kudu/security/token_signer.h
----------------------------------------------------------------------
diff --git a/src/kudu/security/token_signer.h b/src/kudu/security/token_signer.h
index 5bc8a40..9f05d71 100644
--- a/src/kudu/security/token_signer.h
+++ b/src/kudu/security/token_signer.h
@@ -165,8 +165,12 @@ class TokenSigner {
  public:
   // Parameters of the TokenSigner constructor define the TSK rotation 
schedule.
   // See the class's comment just above for details.
+  //
+  // Any newly imported or generated keys are automatically imported into the
+  // passed 'verifier'.
   TokenSigner(int64_t key_validity_seconds,
-              int64_t key_rotation_seconds);
+              int64_t key_rotation_seconds,
+              std::shared_ptr<TokenVerifier> verifier);
   ~TokenSigner();
 
   // Import token signing keys in PB format, notifying TokenVerifier
@@ -230,7 +234,7 @@ class TokenSigner {
                                    std::unique_ptr<TokenSigningPrivateKey>* 
tsk)
       WARN_UNUSED_RESULT;
 
-  std::unique_ptr<TokenVerifier> verifier_;
+  std::shared_ptr<TokenVerifier> verifier_;
 
   // Period of validity for newly created token signing keys. In other words,
   // the expiration time for a new key is set to (now + key_validity_seconds_).

Reply via email to