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
<< " 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(),
#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>
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.
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.
/// 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();
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,
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,
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,
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,
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]
// 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,
// 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,
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]
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,
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)
" 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.
"'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,