]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1706] Checkpoint: code and UT done - todo doc
authorFrancis Dupont <fdupont@isc.org>
Sun, 11 Apr 2021 19:36:57 +0000 (21:36 +0200)
committerFrancis Dupont <fdupont@isc.org>
Tue, 20 Apr 2021 21:26:27 +0000 (23:26 +0200)
src/hooks/dhcp/high_availability/ha_config.cc
src/hooks/dhcp/high_availability/ha_config.h
src/hooks/dhcp/high_availability/ha_service.cc
src/hooks/dhcp/high_availability/tests/Makefile.am
src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc

index 7d71e70debf5c68ef48a2ee275488dece01d4209..bc96e9faef91f0569525b6ccd53852cc9f722d36 100644 (file)
@@ -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(),
index 359c7316e546fcc19c0e732fbc0f8b1c3ef7a18d..adce0594ae4805050c43c34918abca59ffbfd739 100644 (file)
@@ -7,6 +7,7 @@
 #ifndef HA_CONFIG_H
 #define HA_CONFIG_H
 
+#include <asiolink/crypto_tls.h>
 #include <exceptions/exceptions.h>
 #include <http/basic_auth.h>
 #include <http/post_request_json.h>
@@ -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<std::string> trust_anchor_;  ///< Server trust anchor.
         util::Optional<std::string> cert_file_;     ///< Server cert file.
         util::Optional<std::string> 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();
 
index 649f2c204aa83e356e7c7d18d0bbbc491e9a7869..2f9f1644ebd29936acdefaebe992301d3df59056 100644 (file)
@@ -1255,7 +1255,7 @@ HAService::asyncSendLeaseUpdate(const QueryPtrType& query,
     boost::weak_ptr<typename QueryPtrType::element_type> 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<HttpResponseJson>();
 
     // 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<HttpResponseJson>();
 
     // 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<HttpResponseJson>();
 
     // 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<HttpResponseJson>();
 
     // 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<HttpResponseJson>();
 
-    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<HttpResponseJson>();
 
-    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,
index 51d58b2baf5334979db996b74d909cfb1101fabf..2183fc1e6128081018f52f1323c92d9c1eb00c79 100644 (file)
@@ -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)
 
index 68c13eb7899e897d93d0f43fafb8ca78640f811c..566d9a53ae25828d6cae9d6d2165a8d1c6a04565 100644 (file)
@@ -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,