Skip to content

Commit

Permalink
tls: support multiple SSL contexts.
Browse files Browse the repository at this point in the history
This continues the plumbing needed for envoyproxy#1319. In this PR, we add support
to ContextImpl to handle multiple certs during configuration ingestion
and also the skeleton for the certificate switch that occurs following
ClientHello. There is no selection logic yet; this will be a followup
PR.

Risk Level: Low
Testing: bazel test //test/... No new tests, since we are not making a
  major behavioral change, this is refactoring essentially.

Signed-off-by: Harvey Tuch <htuch@google.com>
  • Loading branch information
htuch committed Nov 28, 2018
1 parent bff0167 commit c0de469
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 237 deletions.
2 changes: 1 addition & 1 deletion include/envoy/ssl/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Context {
/**
* @return certificate details conforming to proto admin.v2alpha.certs.
*/
virtual CertificateDetailsPtr getCertChainInformation() const PURE;
virtual std::vector<CertificateDetailsPtr> getCertChainInformation() const PURE;
};
typedef std::shared_ptr<Context> ContextSharedPtr;

Expand Down
512 changes: 286 additions & 226 deletions source/common/ssl/context_impl.cc

Large diffs are not rendered by default.

22 changes: 19 additions & 3 deletions source/common/ssl/context_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class ContextImpl : public virtual Context {
// Ssl::Context
size_t daysUntilFirstCertExpires() const override;
CertificateDetailsPtr getCaCertInformation() const override;
CertificateDetailsPtr getCertChainInformation() const override;
std::vector<CertificateDetailsPtr> getCertChainInformation() const override;

protected:
ContextImpl(Stats::Scope& scope, const ContextConfig& config, TimeSource& time_source);
Expand Down Expand Up @@ -119,11 +119,27 @@ class ContextImpl : public virtual Context {
static SslStats generateStats(Stats::Scope& scope);

std::string getCaFileName() const { return ca_file_path_; };
std::string getCertChainFileName() const { return cert_chain_file_path_; };

CertificateDetailsPtr certificateDetails(X509* cert, const std::string& path) const;

bssl::UniquePtr<SSL_CTX> ctx_;
struct CertificateContext {
// Each certificate specified for the context has its own SSL_CTX. SSL_CTXs
// are identical with the exception of certificate material, and can be
// safely substituted via SSL_set_SSL_CTX() during the
// SSL_CTX_set_select_certificate_cb() callback following ClientHello.
bssl::UniquePtr<SSL_CTX> ssl_ctx_;
bssl::UniquePtr<X509> cert_chain_;
std::string cert_chain_file_path_;
bool ecdsa_{};

std::string getCertChainFileName() const { return cert_chain_file_path_; };
};

// This is always non-empty, with the first context used for all new SSL
// objects. For server contexts, once we have ClientHello, we
// potentially switch to a different CertificateContext based on certificate
// selection.
std::vector<CertificateContext> certificate_contexts_;
bool verify_trusted_ca_{false};
std::vector<std::string> verify_subject_alt_name_list_;
std::vector<std::vector<uint8_t>> verify_certificate_hash_list_;
Expand Down
4 changes: 2 additions & 2 deletions source/server/http/admin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -817,9 +817,9 @@ Http::Code AdminImpl::handlerCerts(absl::string_view, Http::HeaderMap& response_
envoy::admin::v2alpha::CertificateDetails* ca_certificate = certificate.add_ca_cert();
*ca_certificate = *context.getCaCertInformation();
}
if (context.getCertChainInformation() != nullptr) {
for (const auto& cert_details : context.getCertChainInformation()) {
envoy::admin::v2alpha::CertificateDetails* cert_chain = certificate.add_cert_chain();
*cert_chain = *context.getCertChainInformation();
*cert_chain = *cert_details;
}
});
response.add(MessageUtil::getJsonStringFromMessage(certificates, true, true));
Expand Down
8 changes: 5 additions & 3 deletions test/common/ssl/context_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ TEST_F(SslContextImplTest, TestGetCertInformation) {
MessageDifferencer message_differencer;
message_differencer.set_scope(MessageDifferencer::Scope::PARTIAL);
EXPECT_TRUE(message_differencer.Compare(certificate_details, *context->getCaCertInformation()));
EXPECT_TRUE(message_differencer.Compare(cert_chain_details, *context->getCertChainInformation()));
EXPECT_TRUE(
message_differencer.Compare(cert_chain_details, *context->getCertChainInformation()[0]));
}

TEST_F(SslContextImplTest, TestGetCertInformationWithSAN) {
Expand Down Expand Up @@ -223,7 +224,8 @@ TEST_F(SslContextImplTest, TestGetCertInformationWithSAN) {
MessageDifferencer message_differencer;
message_differencer.set_scope(MessageDifferencer::Scope::PARTIAL);
EXPECT_TRUE(message_differencer.Compare(certificate_details, *context->getCaCertInformation()));
EXPECT_TRUE(message_differencer.Compare(cert_chain_details, *context->getCertChainInformation()));
EXPECT_TRUE(
message_differencer.Compare(cert_chain_details, *context->getCertChainInformation()[0]));
}

TEST_F(SslContextImplTest, TestGetCertInformationWithExpiration) {
Expand Down Expand Up @@ -271,7 +273,7 @@ TEST_F(SslContextImplTest, TestNoCert) {
ClientContextConfigImpl cfg(*loader, factory_context_);
ClientContextSharedPtr context(manager_.createSslClientContext(store_, cfg));
EXPECT_EQ(nullptr, context->getCaCertInformation());
EXPECT_EQ(nullptr, context->getCertChainInformation());
EXPECT_TRUE(context->getCertChainInformation().empty());
}

class SslServerContextImplTicketTest : public SslContextImplTest {
Expand Down
2 changes: 1 addition & 1 deletion test/mocks/ssl/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class MockClientContext : public ClientContext {

MOCK_CONST_METHOD0(daysUntilFirstCertExpires, size_t());
MOCK_CONST_METHOD0(getCaCertInformation, CertificateDetailsPtr());
MOCK_CONST_METHOD0(getCertChainInformation, CertificateDetailsPtr());
MOCK_CONST_METHOD0(getCertChainInformation, std::vector<CertificateDetailsPtr>());
};

} // namespace Ssl
Expand Down
2 changes: 1 addition & 1 deletion test/server/http/admin_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,7 @@ TEST_P(AdminInstanceTest, ContextThatReturnsNullCertDetails) {

// Validate that cert details are null and /certs handles it correctly.
EXPECT_EQ(nullptr, client_ctx->getCaCertInformation());
EXPECT_EQ(nullptr, client_ctx->getCertChainInformation());
EXPECT_TRUE(client_ctx->getCertChainInformation().empty());
EXPECT_EQ(Http::Code::OK, getCallback("/certs", header_map, response));
EXPECT_EQ(expected_empty_json, response.toString());
}
Expand Down

0 comments on commit c0de469

Please sign in to comment.