From: Francis Dupont Date: Sun, 11 Apr 2021 19:36:57 +0000 (+0200) Subject: [#1706] Checkpoint: code and UT done - todo doc X-Git-Tag: Kea-1.9.7~41 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=87d99d829ad2bdf10eee0221066dcc40b83cabf6;p=thirdparty%2Fkea.git [#1706] Checkpoint: code and UT done - todo doc --- diff --git a/src/hooks/dhcp/high_availability/ha_config.cc b/src/hooks/dhcp/high_availability/ha_config.cc index 7d71e70deb..bc96e9faef 100644 --- a/src/hooks/dhcp/high_availability/ha_config.cc +++ b/src/hooks/dhcp/high_availability/ha_config.cc @@ -29,7 +29,7 @@ namespace ha { HAConfig::PeerConfig::PeerConfig() : name_(), url_(""), trust_anchor_(), cert_file_(), key_file_(), - role_(STANDBY), auto_failover_(false), basic_auth_() { + tls_context_(), role_(STANDBY), auto_failover_(false), basic_auth_() { } void @@ -334,8 +334,7 @@ HAConfig::validate() { << " is missing or empty: all or none of" << " TLS parameters must be set"); } - TlsContextPtr tls_context; - TlsContext::configure(tls_context, + TlsContext::configure(p->second->getTlsContextNonConst(), TlsRole::CLIENT, ca.get(), cert.get(), diff --git a/src/hooks/dhcp/high_availability/ha_config.h b/src/hooks/dhcp/high_availability/ha_config.h index 359c7316e5..adce0594ae 100644 --- a/src/hooks/dhcp/high_availability/ha_config.h +++ b/src/hooks/dhcp/high_availability/ha_config.h @@ -7,6 +7,7 @@ #ifndef HA_CONFIG_H #define HA_CONFIG_H +#include #include #include #include @@ -135,6 +136,16 @@ public: key_file_ = key; } + /// @brief Return a pointer to the server's TLS context. + asiolink::TlsContextPtr getTlsContext() const { + return (tls_context_); + } + + /// @brief Return a non-const pointer to the server's TLS context. + asiolink::TlsContextPtr& getTlsContextNonConst() { + return (tls_context_); + } + /// @brief Returns a string identifying a server used in logging. /// /// The label is constructed from server name and server URL. @@ -208,6 +219,7 @@ public: util::Optional trust_anchor_; ///< Server trust anchor. util::Optional cert_file_; ///< Server cert file. util::Optional key_file_; ///< Server key file. + asiolink::TlsContextPtr tls_context_; ///< Server TLS context. Role role_; ///< Server role. bool auto_failover_; ///< Auto failover state. http::BasicHttpAuthPtr basic_auth_; ///< Basic HTTP authentication. @@ -713,6 +725,8 @@ public: /// 3. If http-client-threads is 0, it will be replaced with /// the number of DHCP threads /// + /// As a side effect it fills the TLS context of peers when TLS is enabled. + /// /// @throw HAConfigValidationError if configuration is invalid. void validate(); diff --git a/src/hooks/dhcp/high_availability/ha_service.cc b/src/hooks/dhcp/high_availability/ha_service.cc index 649f2c204a..2f9f1644eb 100644 --- a/src/hooks/dhcp/high_availability/ha_service.cc +++ b/src/hooks/dhcp/high_availability/ha_service.cc @@ -1255,7 +1255,7 @@ HAService::asyncSendLeaseUpdate(const QueryPtrType& query, boost::weak_ptr weak_query(query); // Schedule asynchronous HTTP request. - client_.asyncSendRequest(config->getUrl(), TlsContextPtr(), + client_.asyncSendRequest(config->getUrl(), config->getTlsContext(), request, response, [this, weak_query, parking_lot, config] (const boost::system::error_code& ec, @@ -1558,7 +1558,8 @@ HAService::asyncSendHeartbeat() { HttpResponseJsonPtr response = boost::make_shared(); // Schedule asynchronous HTTP request. - client_.asyncSendRequest(partner_config->getUrl(), TlsContextPtr(), + client_.asyncSendRequest(partner_config->getUrl(), + partner_config->getTlsContext(), request, response, [this, partner_config] (const boost::system::error_code& ec, @@ -1700,7 +1701,8 @@ HAService::asyncDisableDHCPService(HttpClient& http_client, HttpResponseJsonPtr response = boost::make_shared(); // Schedule asynchronous HTTP request. - http_client.asyncSendRequest(remote_config->getUrl(), TlsContextPtr(), + http_client.asyncSendRequest(remote_config->getUrl(), + remote_config->getTlsContext(), request, response, [this, remote_config, post_request_action] (const boost::system::error_code& ec, @@ -1775,7 +1777,8 @@ HAService::asyncEnableDHCPService(HttpClient& http_client, HttpResponseJsonPtr response = boost::make_shared(); // Schedule asynchronous HTTP request. - http_client.asyncSendRequest(remote_config->getUrl(), TlsContextPtr(), + http_client.asyncSendRequest(remote_config->getUrl(), + remote_config->getTlsContext(), request, response, [this, remote_config, post_request_action] (const boost::system::error_code& ec, @@ -1918,7 +1921,8 @@ HAService::asyncSyncLeasesInternal(http::HttpClient& http_client, HttpResponseJsonPtr response = boost::make_shared(); // Schedule asynchronous HTTP request. - http_client.asyncSendRequest(partner_config->getUrl(), TlsContextPtr(), + http_client.asyncSendRequest(partner_config->getUrl(), + partner_config->getTlsContext(), request, response, [this, partner_config, post_sync_action, &http_client, server_name, max_period, dhcp_disabled] @@ -2205,7 +2209,7 @@ HAService::asyncSendLeaseUpdatesFromBacklog(HttpClient& http_client, // to know the type of the expected response. HttpResponseJsonPtr response = boost::make_shared(); - http_client.asyncSendRequest(config->getUrl(), TlsContextPtr(), + http_client.asyncSendRequest(config->getUrl(), config->getTlsContext(), request, response, [this, &http_client, config, post_request_action] (const boost::system::error_code& ec, @@ -2305,7 +2309,7 @@ HAService::asyncSendHAReset(HttpClient& http_client, // to know the type of the expected response. HttpResponseJsonPtr response = boost::make_shared(); - http_client.asyncSendRequest(config->getUrl(), TlsContextPtr(), + http_client.asyncSendRequest(config->getUrl(), config->getTlsContext(), request, response, [this, config, post_request_action] (const boost::system::error_code& ec, @@ -2450,7 +2454,8 @@ HAService::processMaintenanceStart() { int captured_rcode = 0; // Schedule asynchronous HTTP request. - client.asyncSendRequest(remote_config->getUrl(), TlsContextPtr(), + client.asyncSendRequest(remote_config->getUrl(), + remote_config->getTlsContext(), request, response, [this, remote_config, &io_service, &captured_ec, &captured_error_message, &captured_rcode] @@ -2571,7 +2576,8 @@ HAService::processMaintenanceCancel() { std::string error_message; // Schedule asynchronous HTTP request. - client.asyncSendRequest(remote_config->getUrl(), TlsContextPtr(), + client.asyncSendRequest(remote_config->getUrl(), + remote_config->getTlsContext(), request, response, [this, remote_config, &io_service, &error_message] (const boost::system::error_code& ec, diff --git a/src/hooks/dhcp/high_availability/tests/Makefile.am b/src/hooks/dhcp/high_availability/tests/Makefile.am index 51d58b2baf..2183fc1e61 100644 --- a/src/hooks/dhcp/high_availability/tests/Makefile.am +++ b/src/hooks/dhcp/high_availability/tests/Makefile.am @@ -5,6 +5,8 @@ AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/high_availability -I$(top_srcdir AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) AM_CPPFLAGS += -DLIBDHCP_HA_SO=\"$(abs_top_builddir)/src/hooks/dhcp/high_availability/.libs/libdhcp_ha.so\" AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" +TEST_CA_DIR = $(srcdir)/../../../../lib/asiolink/testutils/ca +AM_CPPFLAGS += -DTEST_CA_DIR=\"$(TEST_CA_DIR)\" AM_CXXFLAGS = $(KEA_CXXFLAGS) diff --git a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc index 68c13eb789..566d9a53ae 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc @@ -58,6 +58,29 @@ public: " exception type"; } } + + /// @brief Replace a pattern in a configuration. + /// + /// @param config Configuration to patch. + /// @param from String to replace. + /// @param repl String which replaces all occurrences of from. + /// @result A copy of config where all occurrences of from were replaced + /// by repl. + std::string replaceInConfig(const std::string& config, + const std::string& from, + const std::string& repl) { + std::string result(config); + if (from.empty()) { + return (result); + } + for (;;) { + size_t where = result.find(from); + if (where == std::string::npos) { + return (result); + } + result.replace(where, from.size(), repl); + } + } }; // Verifies that load balancing configuration is parsed correctly. @@ -1319,6 +1342,100 @@ TEST_F(HAConfigTest, passiveBackupDelayedUpdatesLimit) { "'delayed-updates-limit' must be set to 0 in the passive backup configuration"); } +#ifndef WITH_BOTAN +/// Test that TLS parameters are correctly inherited. +TEST_F(HAConfigTest, tlsParameterInheritance) { + const std::string ha_config = + "[" + " {" + " \"this-server-name\": \"my-server\"," + " \"mode\": \"load-balancing\"," + " \"trust-anchor\": \"!CA!/kea-ca.crt\"," + " \"cert-file\": \"!CA!/kea-client.crt\"," + " \"key-file\": \"!CA!/kea-client.key\"," + " \"peers\": [" + " {" + " \"name\": \"my-server\"," + " \"url\": \"http://127.0.0.1:8080/\"," + " \"role\": \"primary\"," + " \"auto-failover\": false" + " }," + " {" + " \"name\": \"overwrite\"," + " \"trust-anchor\": \"!CA!\"," + " \"cert-file\": \"!CA!/kea-server.crt\"," + " \"key-file\": \"!CA!/kea-server.key\"," + " \"url\": \"http://127.0.0.1:8080/\"," + " \"role\": \"secondary\"," + " \"auto-failover\": true" + " }," + " {" + " \"name\": \"disable\"," + " \"trust-anchor\": \"\"," + " \"cert-file\": \"\"," + " \"key-file\": \"\"," + " \"url\": \"http://127.0.0.1:8080/\"," + " \"role\": \"backup\"," + " \"auto-failover\": true" + " }" + " ]" + " }" + "]"; + const std::string& patched = replaceInConfig(ha_config, "!CA!", + TEST_CA_DIR); + HAImplPtr impl(new HAImpl()); + ASSERT_NO_THROW(impl->configure(Element::fromJSON(patched))); + + // Check the global parameters. + std::string expected; + EXPECT_FALSE(impl->getConfig()->getTrustAnchor().unspecified()); + expected = TEST_CA_DIR; + expected += "/kea-ca.crt"; + EXPECT_EQ(expected, impl->getConfig()->getTrustAnchor().get()); + EXPECT_FALSE(impl->getConfig()->getCertFile().unspecified()); + expected = TEST_CA_DIR; + expected += "/kea-client.crt"; + EXPECT_EQ(expected, impl->getConfig()->getCertFile().get()); + EXPECT_FALSE(impl->getConfig()->getKeyFile().unspecified()); + expected = TEST_CA_DIR; + expected += "/kea-client.key"; + EXPECT_EQ(expected, impl->getConfig()->getKeyFile().get()); + + // Check the first peer parameters: it inherits them from the global level. + HAConfig::PeerConfigPtr cfg = impl->getConfig()->getThisServerConfig(); + ASSERT_TRUE(cfg); + EXPECT_TRUE(cfg->getTlsContext()); + + // Check the second peer parameters: it overwrites them. + cfg = impl->getConfig()->getPeerConfig("overwrite"); + ASSERT_TRUE(cfg); + EXPECT_FALSE(cfg->getTrustAnchor().unspecified()); + expected = TEST_CA_DIR; + EXPECT_EQ(expected, cfg->getTrustAnchor().get()); + EXPECT_FALSE(cfg->getCertFile().unspecified()); + expected = TEST_CA_DIR; + expected += "/kea-server.crt"; + EXPECT_EQ(expected, cfg->getCertFile().get()); + EXPECT_FALSE(cfg->getKeyFile().unspecified()); + expected = TEST_CA_DIR; + expected += "/kea-server.key"; + EXPECT_EQ(expected, cfg->getKeyFile().get()); + EXPECT_TRUE(cfg->getTlsContext()); + + // Check the last peer parameters: it disables TLS by setting them to "". + cfg = impl->getConfig()->getPeerConfig("disable"); + ASSERT_TRUE(cfg); + EXPECT_FALSE(cfg->getTrustAnchor().unspecified()); + EXPECT_EQ("", cfg->getTrustAnchor().get()); + EXPECT_FALSE(cfg->getCertFile().unspecified()); + EXPECT_EQ("", cfg->getCertFile().get()); + EXPECT_FALSE(cfg->getKeyFile().unspecified()); + EXPECT_EQ("", cfg->getKeyFile().get()); + // The TLS context should be null. + EXPECT_FALSE(cfg->getTlsContext()); +} +#endif + // Test that conversion of the role names works correctly. TEST_F(HAConfigTest, stringToRole) { EXPECT_EQ(HAConfig::PeerConfig::PRIMARY,