2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-01 14:35:29 +00:00

[#3694] apply tls settings on reload

This commit is contained in:
Razvan Becheriu
2025-01-08 20:53:31 +02:00
parent 6fcbddce6f
commit 1334b83c8a
18 changed files with 1932 additions and 84 deletions

View File

@@ -55,7 +55,7 @@ address and port over a TLS channel.
% CTRL_AGENT_HTTP_SERVICE_REUSED reused HTTP service bound to address %1:%2 % CTRL_AGENT_HTTP_SERVICE_REUSED reused HTTP service bound to address %1:%2
This informational message indicates that the server has reused existing This informational message indicates that the server has reused existing
HTTPS service on the specified address and port. HTTP service on the specified address and port.
% CTRL_AGENT_HTTP_SERVICE_STARTED HTTP service bound to address %1:%2 % CTRL_AGENT_HTTP_SERVICE_STARTED HTTP service bound to address %1:%2
This informational message indicates that the server has started HTTP service This informational message indicates that the server has started HTTP service

View File

@@ -139,14 +139,31 @@ CtrlAgentProcess::configure(isc::data::ConstElementPtr config_set,
// Search for the specific connection and reuse the existing one if found. // Search for the specific connection and reuse the existing one if found.
auto it = sockets_.find(std::make_pair(server_address, server_port)); auto it = sockets_.find(std::make_pair(server_address, server_port));
if (it != sockets_.end()) { if (it != sockets_.end()) {
auto listener = getHttpListener(); auto listener = it->second->listener_;
if (listener) { if (listener) {
// Reconfig keeping the same address and port. // Reconfig keeping the same address and port.
if (listener->getTlsContext()) { if (listener->getTlsContext()) {
LOG_INFO(agent_logger, CTRL_AGENT_HTTPS_SERVICE_REUSED) if (ctx->getTrustAnchor().empty()) {
.arg(server_address.toText()) // Can not switch from HTTPS to HTTP
.arg(server_port); LOG_INFO(agent_logger, CTRL_AGENT_HTTPS_SERVICE_REUSED)
} else { .arg(server_address.toText())
.arg(server_port);
} else {
// Apply TLS settings each time.
TlsContextPtr tls_context;
TlsContext::configure(tls_context,
TlsRole::SERVER,
ctx->getTrustAnchor(),
ctx->getCertFile(),
ctx->getKeyFile(),
ctx->getCertRequired());
// Overwrite the authentication setup and the http headers in the response creator config.
it->second->config_->setAuthConfig(ctx->getAuthConfig());
it->second->config_->setHttpHeaders(ctx->getHttpHeaders());
getIOService()->post([listener, tls_context]() { listener->setTlsContext(tls_context); });
}
} else if (!ctx->getTrustAnchor().empty()) {
// Can not switch from HTTP to HTTPS
LOG_INFO(agent_logger, CTRL_AGENT_HTTP_SERVICE_REUSED) LOG_INFO(agent_logger, CTRL_AGENT_HTTP_SERVICE_REUSED)
.arg(server_address.toText()) .arg(server_address.toText())
.arg(server_port); .arg(server_port);

View File

@@ -30,6 +30,7 @@ AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/agent/tests\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
AM_CPPFLAGS += -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples/agent\" AM_CPPFLAGS += -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples/agent\"
AM_CPPFLAGS += -DSYNTAX_FILE=\"$(abs_srcdir)/../agent_parser.yy\" AM_CPPFLAGS += -DSYNTAX_FILE=\"$(abs_srcdir)/../agent_parser.yy\"
AM_CPPFLAGS += -DTEST_CA_DIR=\"$(abs_top_srcdir)/src/lib/asiolink/testutils/ca\"
AM_CXXFLAGS = $(KEA_CXXFLAGS) AM_CXXFLAGS = $(KEA_CXXFLAGS)

View File

@@ -21,6 +21,7 @@
#include <unistd.h> #include <unistd.h>
using namespace isc::asiolink;
using namespace isc::asiolink::test; using namespace isc::asiolink::test;
using namespace isc::agent; using namespace isc::agent;
using namespace isc::data; using namespace isc::data;
@@ -402,7 +403,7 @@ TEST_F(CtrlAgentControllerTest, unsuccessfulConfigUpdate) {
// Tests that it is possible to update the configuration in such a way that the // Tests that it is possible to update the configuration in such a way that the
// listener configuration remains the same. The server should continue using the // listener configuration remains the same. The server should continue using the
// listener instance it has been using prior to the reconfiguration. // listener instance it has been using prior to the reconfiguration.
TEST_F(CtrlAgentControllerTest, noListenerChange) { TEST_F(CtrlAgentControllerTest, noListenerChangeHttp) {
// This configuration should be used to override the initial configuration. // This configuration should be used to override the initial configuration.
const char* second_config = const char* second_config =
"{" "{"
@@ -420,6 +421,8 @@ TEST_F(CtrlAgentControllerTest, noListenerChange) {
" }" " }"
"}"; "}";
const HttpListener* listener_ptr = 0;
// This check callback is called before the shutdown. // This check callback is called before the shutdown.
auto check_callback = [&] { auto check_callback = [&] {
CtrlAgentProcessPtr process = getCtrlAgentProcess(); CtrlAgentProcessPtr process = getCtrlAgentProcess();
@@ -428,6 +431,8 @@ TEST_F(CtrlAgentControllerTest, noListenerChange) {
// Check that the HTTP listener still exists after reconfiguration. // Check that the HTTP listener still exists after reconfiguration.
ConstHttpListenerPtr listener = process->getHttpListener(); ConstHttpListenerPtr listener = process->getHttpListener();
ASSERT_TRUE(listener); ASSERT_TRUE(listener);
ASSERT_EQ(listener_ptr, listener.get());
ASSERT_FALSE(listener->getTlsContext());
EXPECT_TRUE(process->isListening()); EXPECT_TRUE(process->isListening());
EXPECT_EQ("127.0.0.1", listener->getLocalAddress().toText()); EXPECT_EQ("127.0.0.1", listener->getLocalAddress().toText());
@@ -439,6 +444,16 @@ TEST_F(CtrlAgentControllerTest, noListenerChange) {
// Schedule SIGHUP signal to trigger reconfiguration. // Schedule SIGHUP signal to trigger reconfiguration.
TimedSignal sighup(getIOService(), SIGHUP, 200); TimedSignal sighup(getIOService(), SIGHUP, 200);
IntervalTimer timer(getIOService());
timer.setup([&] {
CtrlAgentProcessPtr process = getCtrlAgentProcess();
ASSERT_TRUE(process);
ConstHttpListenerPtr listener = process->getHttpListener();
ASSERT_TRUE(listener);
listener_ptr = listener.get();
ASSERT_FALSE(listener->getTlsContext());
}, 50, IntervalTimer::ONE_SHOT);
// Start the server. // Start the server.
time_duration elapsed_time; time_duration elapsed_time;
runWithConfig(valid_agent_config, 500, runWithConfig(valid_agent_config, 500,
@@ -463,6 +478,292 @@ TEST_F(CtrlAgentControllerTest, noListenerChange) {
EXPECT_FALSE(process->isListening()); EXPECT_FALSE(process->isListening());
} }
// Tests that it is possible to update the configuration in such a way that the
// listener configuration remains the same. The server should continue using the
// listener instance it has been using prior to the reconfiguration.
TEST_F(CtrlAgentControllerTest, noListenerChangeHttps) {
// This configuration should be used to override the initial configuration.
string ca_dir(string(TEST_CA_DIR));
ostringstream agent_st;
agent_st << "{"
<< " \"http-host\": \"127.0.0.1\","
<< " \"http-port\": 8081,"
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\", \n"
<< " \"control-sockets\": {"
<< " \"dhcp4\": {"
<< " \"socket-type\": \"unix\","
<< " \"socket-name\": \"/first/dhcp4/socket\""
<< " },"
<< " \"dhcp6\": {"
<< " \"socket-type\": \"unix\","
<< " \"socket-name\": \"/first/dhcp6/socket\""
<< " }"
<< " }"
<< "}";
ostringstream second_config_st;
second_config_st << "{"
<< " \"http-host\": \"127.0.0.1\","
<< " \"http-port\": 8081,"
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\", \n"
<< " \"control-sockets\": {"
<< " \"dhcp4\": {"
<< " \"socket-type\": \"unix\","
<< " \"socket-name\": \"/second/dhcp4/socket\""
<< " },"
<< " \"dhcp6\": {"
<< " \"socket-type\": \"unix\","
<< " \"socket-name\": \"/second/dhcp6/socket\""
<< " }"
<< " }"
<< "}";
const HttpListener* listener_ptr = 0;
TlsContext* context = 0;
// This check callback is called before the shutdown.
auto check_callback = [&] {
CtrlAgentProcessPtr process = getCtrlAgentProcess();
ASSERT_TRUE(process);
// Check that the HTTP listener still exists after reconfiguration.
ConstHttpListenerPtr listener = process->getHttpListener();
ASSERT_TRUE(listener);
ASSERT_EQ(listener_ptr, listener.get());
ASSERT_TRUE(listener->getTlsContext());
// The TLS settings have been applied
ASSERT_NE(context, listener->getTlsContext().get());
EXPECT_TRUE(process->isListening());
EXPECT_EQ("127.0.0.1", listener->getLocalAddress().toText());
EXPECT_EQ(8081, listener->getLocalPort());
};
// Schedule reconfiguration.
scheduleTimedWrite(second_config_st.str(), 100);
// Schedule SIGHUP signal to trigger reconfiguration.
TimedSignal sighup(getIOService(), SIGHUP, 200);
IntervalTimer timer(getIOService());
timer.setup([&] {
CtrlAgentProcessPtr process = getCtrlAgentProcess();
ASSERT_TRUE(process);
ConstHttpListenerPtr listener = process->getHttpListener();
ASSERT_TRUE(listener);
listener_ptr = listener.get();
ASSERT_TRUE(listener->getTlsContext());
context = listener->getTlsContext().get();
}, 50, IntervalTimer::ONE_SHOT);
// Start the server.
time_duration elapsed_time;
runWithConfig(agent_st.str(), 500,
static_cast<const TestCallback&>(check_callback),
elapsed_time);
CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
ASSERT_TRUE(ctx);
// The server should use a correct listener configuration.
EXPECT_EQ("127.0.0.1", ctx->getHttpHost());
EXPECT_EQ(8081, ctx->getHttpPort());
// The forwarding configuration should have been updated.
testUnixSocketInfo("dhcp4", "/second/dhcp4/socket");
testUnixSocketInfo("dhcp6", "/second/dhcp6/socket");
CtrlAgentProcessPtr process = getCtrlAgentProcess();
ASSERT_TRUE(process);
ConstHttpListenerPtr listener = process->getHttpListener();
ASSERT_FALSE(listener);
EXPECT_FALSE(process->isListening());
}
// Verify that the reload will reuse listener
TEST_F(CtrlAgentControllerTest, ignoreHttpToHttpsSwitch) {
string ca_dir(string(TEST_CA_DIR));
// This configuration should be used to override the initial configuration.
ostringstream second_config_st;
second_config_st << "{"
<< " \"http-host\": \"127.0.0.1\","
<< " \"http-port\": 8081,"
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\", \n"
<< " \"control-sockets\": {"
<< " \"dhcp4\": {"
<< " \"socket-type\": \"unix\","
<< " \"socket-name\": \"/second/dhcp4/socket\""
<< " },"
<< " \"dhcp6\": {"
<< " \"socket-type\": \"unix\","
<< " \"socket-name\": \"/second/dhcp6/socket\""
<< " }"
<< " }"
<< "}";
const HttpListener* listener_ptr = 0;
// This check callback is called before the shutdown.
auto check_callback = [&] {
CtrlAgentProcessPtr process = getCtrlAgentProcess();
ASSERT_TRUE(process);
// Check that the HTTP listener still exists after reconfiguration.
ConstHttpListenerPtr listener = process->getHttpListener();
ASSERT_TRUE(listener);
ASSERT_EQ(listener_ptr, listener.get());
ASSERT_FALSE(listener->getTlsContext());
EXPECT_TRUE(process->isListening());
EXPECT_EQ("127.0.0.1", listener->getLocalAddress().toText());
EXPECT_EQ(8081, listener->getLocalPort());
};
// Schedule reconfiguration.
scheduleTimedWrite(second_config_st.str(), 100);
// Schedule SIGHUP signal to trigger reconfiguration.
TimedSignal sighup(getIOService(), SIGHUP, 200);
IntervalTimer timer(getIOService());
timer.setup([&] {
CtrlAgentProcessPtr process = getCtrlAgentProcess();
ASSERT_TRUE(process);
ConstHttpListenerPtr listener = process->getHttpListener();
ASSERT_TRUE(listener);
listener_ptr = listener.get();
ASSERT_FALSE(listener->getTlsContext());
}, 50, IntervalTimer::ONE_SHOT);
// Start the server.
time_duration elapsed_time;
runWithConfig(valid_agent_config, 500,
static_cast<const TestCallback&>(check_callback),
elapsed_time);
CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
ASSERT_TRUE(ctx);
// The server should use a correct listener configuration.
EXPECT_EQ("127.0.0.1", ctx->getHttpHost());
EXPECT_EQ(8081, ctx->getHttpPort());
// The forwarding configuration should have been updated.
testUnixSocketInfo("dhcp4", "/second/dhcp4/socket");
testUnixSocketInfo("dhcp6", "/second/dhcp6/socket");
CtrlAgentProcessPtr process = getCtrlAgentProcess();
ASSERT_TRUE(process);
ConstHttpListenerPtr listener = process->getHttpListener();
ASSERT_FALSE(listener);
EXPECT_FALSE(process->isListening());
}
// Verify that the reload will reuse listener
TEST_F(CtrlAgentControllerTest, ignoreHttpsToHttpSwitch) {
string ca_dir(string(TEST_CA_DIR));
ostringstream agent_st;
agent_st << "{"
<< " \"http-host\": \"127.0.0.1\","
<< " \"http-port\": 8081,"
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\", \n"
<< " \"control-sockets\": {"
<< " \"dhcp4\": {"
<< " \"socket-type\": \"unix\","
<< " \"socket-name\": \"/first/dhcp4/socket\""
<< " },"
<< " \"dhcp6\": {"
<< " \"socket-type\": \"unix\","
<< " \"socket-name\": \"/first/dhcp6/socket\""
<< " }"
<< " }"
<< "}";
// This configuration should be used to override the initial configuration.
ostringstream second_config_st;
second_config_st << "{"
<< " \"http-host\": \"127.0.0.1\","
<< " \"http-port\": 8081,"
<< " \"control-sockets\": {"
<< " \"dhcp4\": {"
<< " \"socket-type\": \"unix\","
<< " \"socket-name\": \"/second/dhcp4/socket\""
<< " },"
<< " \"dhcp6\": {"
<< " \"socket-type\": \"unix\","
<< " \"socket-name\": \"/second/dhcp6/socket\""
<< " }"
<< " }"
<< "}";
const HttpListener* listener_ptr = 0;
TlsContext* context = 0;
// This check callback is called before the shutdown.
auto check_callback = [&] {
CtrlAgentProcessPtr process = getCtrlAgentProcess();
ASSERT_TRUE(process);
// Check that the HTTP listener still exists after reconfiguration.
ConstHttpListenerPtr listener = process->getHttpListener();
ASSERT_TRUE(listener);
ASSERT_EQ(listener_ptr, listener.get());
ASSERT_TRUE(listener->getTlsContext());
// The TLS settings have not changed
ASSERT_EQ(context, listener->getTlsContext().get());
EXPECT_TRUE(process->isListening());
EXPECT_EQ("127.0.0.1", listener->getLocalAddress().toText());
EXPECT_EQ(8081, listener->getLocalPort());
};
// Schedule reconfiguration.
scheduleTimedWrite(second_config_st.str(), 100);
// Schedule SIGHUP signal to trigger reconfiguration.
TimedSignal sighup(getIOService(), SIGHUP, 200);
IntervalTimer timer(getIOService());
timer.setup([&] {
CtrlAgentProcessPtr process = getCtrlAgentProcess();
ASSERT_TRUE(process);
ConstHttpListenerPtr listener = process->getHttpListener();
ASSERT_TRUE(listener);
listener_ptr = listener.get();
ASSERT_TRUE(listener->getTlsContext());
context = listener->getTlsContext().get();
}, 50, IntervalTimer::ONE_SHOT);
// Start the server.
time_duration elapsed_time;
runWithConfig(agent_st.str(), 500,
static_cast<const TestCallback&>(check_callback),
elapsed_time);
CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
ASSERT_TRUE(ctx);
// The server should use a correct listener configuration.
EXPECT_EQ("127.0.0.1", ctx->getHttpHost());
EXPECT_EQ(8081, ctx->getHttpPort());
// The forwarding configuration should have been updated.
testUnixSocketInfo("dhcp4", "/second/dhcp4/socket");
testUnixSocketInfo("dhcp6", "/second/dhcp6/socket");
CtrlAgentProcessPtr process = getCtrlAgentProcess();
ASSERT_TRUE(process);
ConstHttpListenerPtr listener = process->getHttpListener();
ASSERT_FALSE(listener);
EXPECT_FALSE(process->isListening());
}
// Tests that registerCommands actually registers anything. // Tests that registerCommands actually registers anything.
TEST_F(CtrlAgentControllerTest, registeredCommands) { TEST_F(CtrlAgentControllerTest, registeredCommands) {
ASSERT_NO_THROW(initProcess()); ASSERT_NO_THROW(initProcess());

View File

@@ -522,16 +522,11 @@ D2Process::reconfigureCommandChannel() {
ConstElementPtr http_config = ConstElementPtr http_config =
getD2CfgMgr()->getHttpControlSocketInfo(); getD2CfgMgr()->getHttpControlSocketInfo();
sock_changed = (http_config && current_http_control_socket_ && // Open the new sockets and close old ones, keeping reused.
!http_config->equals(*current_http_control_socket_)); if (http_config) {
HttpCommandMgr::instance().openCommandSockets(http_config);
if (!http_config || !current_http_control_socket_ || sock_changed) { } else if (current_http_control_socket_) {
// Open the new sockets and close old ones, keeping reused. HttpCommandMgr::instance().closeCommandSockets();
if (http_config) {
HttpCommandMgr::instance().openCommandSockets(http_config);
} else if (current_http_control_socket_) {
HttpCommandMgr::instance().closeCommandSockets();
}
} }
// Commit the new socket configuration. // Commit the new socket configuration.

View File

@@ -1218,7 +1218,7 @@ TEST_F(HttpCtrlChannelD2Test, configSet) {
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()); ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
// Create a config with invalid content that should fail to parse. // Create a config with invalid content that should fail to parse.
string config_test_txt = string config_set_txt =
"{ \"command\": \"config-set\", \n" "{ \"command\": \"config-set\", \n"
" \"arguments\": { \n" " \"arguments\": { \n"
" \"DhcpDdns\": \n" " \"DhcpDdns\": \n"
@@ -1242,7 +1242,7 @@ TEST_F(HttpCtrlChannelD2Test, configSet) {
// Send the config-set command. // Send the config-set command.
string response; string response;
sendHttpCommand(config_test_txt, response); sendHttpCommand(config_set_txt, response);
// Should fail with a syntax error. // Should fail with a syntax error.
EXPECT_EQ("[ { \"result\": 1, \"text\": \"missing parameter 'name' (<string>:10:14)\" } ]", EXPECT_EQ("[ { \"result\": 1, \"text\": \"missing parameter 'name' (<string>:10:14)\" } ]",
@@ -1255,7 +1255,7 @@ TEST_F(HttpCtrlChannelD2Test, configSet) {
EXPECT_EQ(1, keys->size()); EXPECT_EQ(1, keys->size());
// Create a valid config with two keys and no command channel. // Create a valid config with two keys and no command channel.
config_test_txt = config_set_txt =
"{ \"command\": \"config-set\", \n" "{ \"command\": \"config-set\", \n"
" \"arguments\": { \n" " \"arguments\": { \n"
" \"DhcpDdns\": \n" " \"DhcpDdns\": \n"
@@ -1280,7 +1280,7 @@ TEST_F(HttpCtrlChannelD2Test, configSet) {
EXPECT_TRUE(HttpCommandMgr::instance().getHttpListener()); EXPECT_TRUE(HttpCommandMgr::instance().getHttpListener());
// Send the config-set command. // Send the config-set command.
sendHttpCommand(config_test_txt, response); sendHttpCommand(config_set_txt, response);
// Verify the HTTP control channel socket no longer exists. // Verify the HTTP control channel socket no longer exists.
ASSERT_NO_THROW(HttpCommandMgr::instance().closeCommandSockets()); ASSERT_NO_THROW(HttpCommandMgr::instance().closeCommandSockets());
@@ -1355,7 +1355,7 @@ TEST_F(HttpsCtrlChannelD2Test, configSet) {
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()); ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
// Create a config with invalid content that should fail to parse. // Create a config with invalid content that should fail to parse.
string config_test_txt = string config_set_txt =
"{ \"command\": \"config-set\", \n" "{ \"command\": \"config-set\", \n"
" \"arguments\": { \n" " \"arguments\": { \n"
" \"DhcpDdns\": \n" " \"DhcpDdns\": \n"
@@ -1379,7 +1379,7 @@ TEST_F(HttpsCtrlChannelD2Test, configSet) {
// Send the config-set command. // Send the config-set command.
string response; string response;
sendHttpCommand(config_test_txt, response); sendHttpCommand(config_set_txt, response);
// Should fail with a syntax error. // Should fail with a syntax error.
EXPECT_EQ("[ { \"result\": 1, \"text\": \"missing parameter 'name' (<string>:10:14)\" } ]", EXPECT_EQ("[ { \"result\": 1, \"text\": \"missing parameter 'name' (<string>:10:14)\" } ]",
@@ -1392,7 +1392,7 @@ TEST_F(HttpsCtrlChannelD2Test, configSet) {
EXPECT_EQ(1, keys->size()); EXPECT_EQ(1, keys->size());
// Create a valid config with two keys and no command channel. // Create a valid config with two keys and no command channel.
config_test_txt = config_set_txt =
"{ \"command\": \"config-set\", \n" "{ \"command\": \"config-set\", \n"
" \"arguments\": { \n" " \"arguments\": { \n"
" \"DhcpDdns\": \n" " \"DhcpDdns\": \n"
@@ -1417,7 +1417,7 @@ TEST_F(HttpsCtrlChannelD2Test, configSet) {
EXPECT_TRUE(HttpCommandMgr::instance().getHttpListener()); EXPECT_TRUE(HttpCommandMgr::instance().getHttpListener());
// Send the config-set command. // Send the config-set command.
sendHttpCommand(config_test_txt, response); sendHttpCommand(config_set_txt, response);
// Verify the HTTP control channel socket no longer exists. // Verify the HTTP control channel socket no longer exists.
ASSERT_NO_THROW(HttpCommandMgr::instance().closeCommandSockets()); ASSERT_NO_THROW(HttpCommandMgr::instance().closeCommandSockets());
@@ -1984,4 +1984,374 @@ TEST_F(HttpsCtrlChannelD2Test, connectionTimeoutNoData) {
testConnectionTimeoutNoData(); testConnectionTimeoutNoData();
} }
// Verify that the "config-set" command will reuse listener
TEST_F(HttpCtrlChannelD2Test, noListenerChange) {
string d2_cfg_txt =
" { \n"
" \"ip-address\": \"192.168.77.1\", \n"
" \"port\": 777, \n"
" \"forward-ddns\" : {}, \n"
" \"reverse-ddns\" : {}, \n"
" \"tsig-keys\": [ \n"
" {\"name\": \"d2_key.example.com\", \n"
" \"algorithm\": \"hmac-md5\", \n"
" \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"
" ], \n"
" \"control-socket\": { \n"
" \"socket-type\": \"http\", \n"
" \"socket-address\": \"127.0.0.1\", \n"
" \"socket-port\": 18125 \n"
" } \n"
" } \n";
ASSERT_TRUE(server_);
ConstElementPtr config;
ASSERT_NO_THROW(config = parseDHCPDDNS(d2_cfg_txt, true));
ASSERT_NO_THROW(d2Controller()->initProcess());
D2ProcessPtr proc = d2Controller()->getProcess();
ASSERT_TRUE(proc);
ConstElementPtr answer = proc->configure(config, false);
ASSERT_TRUE(answer);
EXPECT_EQ("{ \"arguments\": { \"hash\": \"029AE1208415D6911B5651A6F82D054F55B7877D2589CFD1DCEB5BFFCD3B13A3\" }, \"result\": 0, \"text\": \"Configuration applied successfully.\" }",
answer->str());
ASSERT_NO_THROW(d2Controller()->registerCommands());
// Check that the config was indeed applied.
D2CfgMgrPtr cfg_mgr = proc->getD2CfgMgr();
ASSERT_TRUE(cfg_mgr);
D2CfgContextPtr d2_context = cfg_mgr->getD2CfgContext();
ASSERT_TRUE(d2_context);
TSIGKeyInfoMapPtr keys = d2_context->getKeys();
ASSERT_TRUE(keys);
EXPECT_EQ(1, keys->size());
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
// Create a config with same content that should not recreate listener.
string config_set_txt =
"{ \"command\": \"config-set\", \n"
" \"arguments\": { \n"
" \"DhcpDdns\": \n";
config_set_txt += d2_cfg_txt;
config_set_txt += "}} \n";
// Send the config-set command.
string response;
sendHttpCommand(config_set_txt, response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
// Verify the configuration was successful.
EXPECT_EQ("[ { \"arguments\": { \"hash\": \"029AE1208415D6911B5651A6F82D054F55B7877D2589CFD1DCEB5BFFCD3B13A3\" }, \"result\": 0, \"text\": \"Configuration applied successfully.\" } ]",
response);
// Check that the config was applied.
d2_context = cfg_mgr->getD2CfgContext();
keys = d2_context->getKeys();
ASSERT_TRUE(keys);
EXPECT_EQ(1, keys->size());
}
// Verify that the "config-set" command will reuse listener
TEST_F(HttpsCtrlChannelD2Test, noListenerChange) {
string ca_dir(string(TEST_CA_DIR));
ostringstream d2_st;
d2_st << " { \n"
<< " \"ip-address\": \"192.168.77.1\", \n"
<< " \"port\": 777, \n"
<< " \"forward-ddns\" : {}, \n"
<< " \"reverse-ddns\" : {}, \n"
<< " \"tsig-keys\": [ \n"
<< " {\"name\": \"d2_key.example.com\", \n"
<< " \"algorithm\": \"hmac-md5\", \n"
<< " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"
<< " ], \n"
<< " \"control-socket\": { \n"
<< " \"socket-type\": \"https\", \n"
<< " \"socket-address\": \"127.0.0.1\", \n"
<< " \"socket-port\": 18125, \n"
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\" \n"
<< " } \n"
<< " } \n";
ASSERT_TRUE(server_);
ConstElementPtr config;
ASSERT_NO_THROW(config = parseDHCPDDNS(d2_st.str(), true));
ASSERT_NO_THROW(d2Controller()->initProcess());
D2ProcessPtr proc = d2Controller()->getProcess();
ASSERT_TRUE(proc);
ConstElementPtr answer = proc->configure(config, false);
ASSERT_TRUE(answer);
// Verify the configuration was successful. The config contains random
// file paths (CA directory), so the hash will be different each time.
// As such, we can do simplified checks:
// - verify the "result": 0 is there
// - verify the "text": "Configuration applied successfully." is there
string answer_txt = answer->str();
EXPECT_NE(answer_txt.find("\"result\": 0"), std::string::npos);
EXPECT_NE(answer_txt.find("\"text\": \"Configuration applied successfully.\""),
std::string::npos);
ASSERT_NO_THROW(d2Controller()->registerCommands());
// Check that the config was indeed applied.
D2CfgMgrPtr cfg_mgr = proc->getD2CfgMgr();
ASSERT_TRUE(cfg_mgr);
D2CfgContextPtr d2_context = cfg_mgr->getD2CfgContext();
ASSERT_TRUE(d2_context);
TSIGKeyInfoMapPtr keys = d2_context->getKeys();
ASSERT_TRUE(keys);
EXPECT_EQ(1, keys->size());
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
auto const context = HttpCommandMgr::instance().getHttpListener()->getTlsContext().get();
// Create a config with same content that should not recreate listener.
string config_set_txt =
"{ \"command\": \"config-set\", \n"
" \"arguments\": { \n"
" \"DhcpDdns\": \n";
config_set_txt += d2_st.str();
config_set_txt += "}} \n";
// Send the config-set command.
string response;
sendHttpCommand(config_set_txt, response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
// The TLS settings have been applied
EXPECT_NE(context, HttpCommandMgr::instance().getHttpListener()->getTlsContext().get());
// Verify the configuration was successful.
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration applied successfully.\""),
std::string::npos);
// Check that the config was applied.
d2_context = cfg_mgr->getD2CfgContext();
keys = d2_context->getKeys();
ASSERT_TRUE(keys);
EXPECT_EQ(1, keys->size());
}
// Verify that the "config-set" command will reuse listener
TEST_F(HttpCtrlChannelD2Test, ignoreHttpToHttpsSwitch) {
string d2_cfg_txt =
" { \n"
" \"ip-address\": \"192.168.77.1\", \n"
" \"port\": 777, \n"
" \"forward-ddns\" : {}, \n"
" \"reverse-ddns\" : {}, \n"
" \"tsig-keys\": [ \n"
" {\"name\": \"d2_key.example.com\", \n"
" \"algorithm\": \"hmac-md5\", \n"
" \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"
" ], \n"
" \"control-socket\": { \n"
" \"socket-type\": \"http\", \n"
" \"socket-address\": \"127.0.0.1\", \n"
" \"socket-port\": 18125 \n"
" } \n"
" } \n";
ASSERT_TRUE(server_);
ConstElementPtr config;
ASSERT_NO_THROW(config = parseDHCPDDNS(d2_cfg_txt, true));
ASSERT_NO_THROW(d2Controller()->initProcess());
D2ProcessPtr proc = d2Controller()->getProcess();
ASSERT_TRUE(proc);
ConstElementPtr answer = proc->configure(config, false);
ASSERT_TRUE(answer);
EXPECT_EQ("{ \"arguments\": { \"hash\": \"029AE1208415D6911B5651A6F82D054F55B7877D2589CFD1DCEB5BFFCD3B13A3\" }, \"result\": 0, \"text\": \"Configuration applied successfully.\" }",
answer->str());
ASSERT_NO_THROW(d2Controller()->registerCommands());
// Check that the config was indeed applied.
D2CfgMgrPtr cfg_mgr = proc->getD2CfgMgr();
ASSERT_TRUE(cfg_mgr);
D2CfgContextPtr d2_context = cfg_mgr->getD2CfgContext();
ASSERT_TRUE(d2_context);
TSIGKeyInfoMapPtr keys = d2_context->getKeys();
ASSERT_TRUE(keys);
EXPECT_EQ(1, keys->size());
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
string ca_dir(string(TEST_CA_DIR));
ostringstream d2_st;
d2_st << " { \n"
<< " \"ip-address\": \"192.168.77.1\", \n"
<< " \"port\": 777, \n"
<< " \"forward-ddns\" : {}, \n"
<< " \"reverse-ddns\" : {}, \n"
<< " \"tsig-keys\": [ \n"
<< " {\"name\": \"d2_key.example.com\", \n"
<< " \"algorithm\": \"hmac-md5\", \n"
<< " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"
<< " ], \n"
<< " \"control-socket\": { \n"
<< " \"socket-type\": \"https\", \n"
<< " \"socket-address\": \"127.0.0.1\", \n"
<< " \"socket-port\": 18125, \n"
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\" \n"
<< " } \n"
<< " } \n";
// Create a config with HTTPS and same content that should not recreate listener.
string config_set_txt =
"{ \"command\": \"config-set\", \n"
" \"arguments\": { \n"
" \"DhcpDdns\": \n";
config_set_txt += d2_st.str();
config_set_txt += "}} \n";
// Send the config-set command.
string response;
sendHttpCommand(config_set_txt, response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
// Verify the configuration was successful.
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration applied successfully.\""),
std::string::npos);
// Check that the config was applied.
d2_context = cfg_mgr->getD2CfgContext();
keys = d2_context->getKeys();
ASSERT_TRUE(keys);
EXPECT_EQ(1, keys->size());
}
// Verify that the "config-set" command will reuse listener
TEST_F(HttpsCtrlChannelD2Test, ignoreHttpsToHttpSwitch) {
string ca_dir(string(TEST_CA_DIR));
ostringstream d2_st;
d2_st << " { \n"
<< " \"ip-address\": \"192.168.77.1\", \n"
<< " \"port\": 777, \n"
<< " \"forward-ddns\" : {}, \n"
<< " \"reverse-ddns\" : {}, \n"
<< " \"tsig-keys\": [ \n"
<< " {\"name\": \"d2_key.example.com\", \n"
<< " \"algorithm\": \"hmac-md5\", \n"
<< " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"
<< " ], \n"
<< " \"control-socket\": { \n"
<< " \"socket-type\": \"https\", \n"
<< " \"socket-address\": \"127.0.0.1\", \n"
<< " \"socket-port\": 18125, \n"
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\" \n"
<< " } \n"
<< " } \n";
ASSERT_TRUE(server_);
ConstElementPtr config;
ASSERT_NO_THROW(config = parseDHCPDDNS(d2_st.str(), true));
ASSERT_NO_THROW(d2Controller()->initProcess());
D2ProcessPtr proc = d2Controller()->getProcess();
ASSERT_TRUE(proc);
ConstElementPtr answer = proc->configure(config, false);
ASSERT_TRUE(answer);
// Verify the configuration was successful. The config contains random
// file paths (CA directory), so the hash will be different each time.
// As such, we can do simplified checks:
// - verify the "result": 0 is there
// - verify the "text": "Configuration applied successfully." is there
string answer_txt = answer->str();
EXPECT_NE(answer_txt.find("\"result\": 0"), std::string::npos);
EXPECT_NE(answer_txt.find("\"text\": \"Configuration applied successfully.\""),
std::string::npos);
ASSERT_NO_THROW(d2Controller()->registerCommands());
// Check that the config was indeed applied.
D2CfgMgrPtr cfg_mgr = proc->getD2CfgMgr();
ASSERT_TRUE(cfg_mgr);
D2CfgContextPtr d2_context = cfg_mgr->getD2CfgContext();
ASSERT_TRUE(d2_context);
TSIGKeyInfoMapPtr keys = d2_context->getKeys();
ASSERT_TRUE(keys);
EXPECT_EQ(1, keys->size());
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
auto const context = HttpCommandMgr::instance().getHttpListener()->getTlsContext().get();
string d2_cfg_txt =
" { \n"
" \"ip-address\": \"192.168.77.1\", \n"
" \"port\": 777, \n"
" \"forward-ddns\" : {}, \n"
" \"reverse-ddns\" : {}, \n"
" \"tsig-keys\": [ \n"
" {\"name\": \"d2_key.example.com\", \n"
" \"algorithm\": \"hmac-md5\", \n"
" \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"
" ], \n"
" \"control-socket\": { \n"
" \"socket-type\": \"http\", \n"
" \"socket-address\": \"127.0.0.1\", \n"
" \"socket-port\": 18125 \n"
" } \n"
" } \n";
// Create a config with HTTP and same content that should not recreate listener.
string config_set_txt =
"{ \"command\": \"config-set\", \n"
" \"arguments\": { \n"
" \"DhcpDdns\": \n";
config_set_txt += d2_cfg_txt;
config_set_txt += "}} \n";
// Send the config-set command.
string response;
sendHttpCommand(config_set_txt, response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
// The TLS settings have not changed
EXPECT_EQ(context, HttpCommandMgr::instance().getHttpListener()->getTlsContext().get());
// Verify the configuration was successful.
EXPECT_EQ("[ { \"arguments\": { \"hash\": \"029AE1208415D6911B5651A6F82D054F55B7877D2589CFD1DCEB5BFFCD3B13A3\" }, \"result\": 0, \"text\": \"Configuration applied successfully.\" } ]",
response);
// Check that the config was applied.
d2_context = cfg_mgr->getD2CfgContext();
keys = d2_context->getKeys();
ASSERT_TRUE(keys);
EXPECT_EQ(1, keys->size());
}
} // end of anonymous namespace } // end of anonymous namespace

View File

@@ -338,15 +338,10 @@ void configureCommandChannel() {
ConstElementPtr current_http_config = ConstElementPtr current_http_config =
CfgMgr::instance().getCurrentCfg()->getHttpControlSocketInfo(); CfgMgr::instance().getCurrentCfg()->getHttpControlSocketInfo();
sock_changed = (http_config && current_http_config && if (http_config) {
!http_config->equals(*current_http_config)); HttpCommandMgr::instance().openCommandSockets(http_config);
} else if (current_http_config) {
if (!http_config || !current_http_config || sock_changed) { HttpCommandMgr::instance().closeCommandSockets();
if (http_config) {
HttpCommandMgr::instance().openCommandSockets(http_config);
} else if (current_http_config) {
HttpCommandMgr::instance().closeCommandSockets();
}
} }
} }

View File

@@ -983,7 +983,7 @@ TEST_F(HttpCtrlChannelDhcpv4Test, configSet) {
// Define strings to permutate the config arguments // Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find) // (Note the line feeds makes errors easy to find)
string set_config_txt = "{ \"command\": \"config-set\" \n"; string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n"; string args_txt = " \"arguments\": { \n";
string dhcp4_cfg_txt = string dhcp4_cfg_txt =
" \"Dhcp4\": { \n" " \"Dhcp4\": { \n"
@@ -1056,7 +1056,7 @@ TEST_F(HttpCtrlChannelDhcpv4Test, configSet) {
std::ostringstream os; std::ostringstream os;
// Create a valid config with all the parts should parse // Create a valid config with all the parts should parse
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp4_cfg_txt << dhcp4_cfg_txt
<< subnet1 << subnet1
@@ -1085,7 +1085,7 @@ TEST_F(HttpCtrlChannelDhcpv4Test, configSet) {
// Create a config with malformed subnet that should fail to parse. // Create a config with malformed subnet that should fail to parse.
os.str(""); os.str("");
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp4_cfg_txt << dhcp4_cfg_txt
<< bad_subnet << bad_subnet
@@ -1114,7 +1114,7 @@ TEST_F(HttpCtrlChannelDhcpv4Test, configSet) {
// Create a valid config with two subnets and no command channel. // Create a valid config with two subnets and no command channel.
// It should succeed, client should still receive the response // It should succeed, client should still receive the response
os.str(""); os.str("");
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp4_cfg_txt << dhcp4_cfg_txt
<< subnet1 << subnet1
@@ -1153,7 +1153,7 @@ TEST_F(HttpsCtrlChannelDhcpv4Test, configSet) {
// Define strings to permutate the config arguments // Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find) // (Note the line feeds makes errors easy to find)
string ca_dir(string(TEST_CA_DIR)); string ca_dir(string(TEST_CA_DIR));
string set_config_txt = "{ \"command\": \"config-set\" \n"; string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n"; string args_txt = " \"arguments\": { \n";
string dhcp4_cfg_txt = string dhcp4_cfg_txt =
" \"Dhcp4\": { \n" " \"Dhcp4\": { \n"
@@ -1227,7 +1227,7 @@ TEST_F(HttpsCtrlChannelDhcpv4Test, configSet) {
std::ostringstream os; std::ostringstream os;
// Create a valid config with all the parts should parse // Create a valid config with all the parts should parse
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp4_cfg_txt << dhcp4_cfg_txt
<< subnet1 << subnet1
@@ -1266,7 +1266,7 @@ TEST_F(HttpsCtrlChannelDhcpv4Test, configSet) {
// Create a config with malformed subnet that should fail to parse. // Create a config with malformed subnet that should fail to parse.
os.str(""); os.str("");
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp4_cfg_txt << dhcp4_cfg_txt
<< bad_subnet << bad_subnet
@@ -1299,7 +1299,7 @@ TEST_F(HttpsCtrlChannelDhcpv4Test, configSet) {
// Create a valid config with two subnets and no command channel. // Create a valid config with two subnets and no command channel.
// It should succeed, client should still receive the response // It should succeed, client should still receive the response
os.str(""); os.str("");
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp4_cfg_txt << dhcp4_cfg_txt
<< subnet1 << subnet1
@@ -1411,7 +1411,7 @@ TEST_F(HttpCtrlChannelDhcpv4Test, configTest) {
// Define strings to permutate the config arguments // Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find) // (Note the line feeds makes errors easy to find)
string set_config_txt = "{ \"command\": \"config-set\" \n"; string config_set_txt = "{ \"command\": \"config-set\" \n";
string config_test_txt = "{ \"command\": \"config-test\" \n"; string config_test_txt = "{ \"command\": \"config-test\" \n";
string args_txt = " \"arguments\": { \n"; string args_txt = " \"arguments\": { \n";
string dhcp4_cfg_txt = string dhcp4_cfg_txt =
@@ -1463,7 +1463,7 @@ TEST_F(HttpCtrlChannelDhcpv4Test, configTest) {
std::ostringstream os; std::ostringstream os;
// Create a valid config with all the parts should parse // Create a valid config with all the parts should parse
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp4_cfg_txt << dhcp4_cfg_txt
<< subnet1 << subnet1
@@ -1552,7 +1552,7 @@ TEST_F(HttpsCtrlChannelDhcpv4Test, configTest) {
// Define strings to permutate the config arguments // Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find) // (Note the line feeds makes errors easy to find)
string ca_dir(string(TEST_CA_DIR)); string ca_dir(string(TEST_CA_DIR));
string set_config_txt = "{ \"command\": \"config-set\" \n"; string config_set_txt = "{ \"command\": \"config-set\" \n";
string config_test_txt = "{ \"command\": \"config-test\" \n"; string config_test_txt = "{ \"command\": \"config-test\" \n";
string args_txt = " \"arguments\": { \n"; string args_txt = " \"arguments\": { \n";
string dhcp4_cfg_txt = string dhcp4_cfg_txt =
@@ -1605,7 +1605,7 @@ TEST_F(HttpsCtrlChannelDhcpv4Test, configTest) {
std::ostringstream os; std::ostringstream os;
// Create a valid config with all the parts should parse // Create a valid config with all the parts should parse
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp4_cfg_txt << dhcp4_cfg_txt
<< subnet1 << subnet1
@@ -3392,4 +3392,567 @@ TEST_F(HttpsCtrlChannelDhcpv4Test, connectionTimeoutNoData) {
testConnectionTimeoutNoData(); testConnectionTimeoutNoData();
} }
// Verify that the "config-set" command will reuse listener
TEST_F(HttpCtrlChannelDhcpv4Test, noListenerChange) {
createHttpChannelServer();
// Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find)
string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n";
string dhcp4_cfg_txt =
" \"Dhcp4\": { \n"
" \"interfaces-config\": { \n"
" \"interfaces\": [\"*\"] \n"
" }, \n"
" \"valid-lifetime\": 4000, \n"
" \"renew-timer\": 1000, \n"
" \"rebind-timer\": 2000, \n"
" \"lease-database\": { \n"
" \"type\": \"memfile\", \n"
" \"persist\":false, \n"
" \"lfc-interval\": 0 \n"
" }, \n"
" \"expired-leases-processing\": { \n"
" \"reclaim-timer-wait-time\": 0, \n"
" \"hold-reclaimed-time\": 0, \n"
" \"flush-reclaimed-timer-wait-time\": 0 \n"
" },"
" \"subnet4\": [ \n";
string subnet1 =
" {\"subnet\": \"192.2.0.0/24\", \"id\": 1, \n"
" \"pools\": [{ \"pool\": \"192.2.0.1-192.2.0.50\" }]}\n";
string subnet_footer =
" ] \n";
string option_def =
" ,\"option-def\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"type\": \"uint32\",\n"
" \"array\": false,\n"
" \"record-types\": \"\",\n"
" \"space\": \"dhcp4\",\n"
" \"encapsulate\": \"\"\n"
" }\n"
"]\n";
string option_data =
" ,\"option-data\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"space\": \"dhcp4\",\n"
" \"csv-format\": true,\n"
" \"data\": \"12345\"\n"
" }\n"
"]\n";
string control_socket =
" ,\"control-socket\": { \n"
" \"socket-type\": \"http\", \n"
" \"socket-address\": \"127.0.0.1\", \n"
" \"socket-port\": 18124 \n"
" } \n";
string logger_txt =
" ,\"loggers\": [ { \n"
" \"name\": \"kea\", \n"
" \"severity\": \"FATAL\", \n"
" \"output-options\": [{ \n"
" \"output\": \"/dev/null\", \n"
" \"maxsize\": 0"
" }] \n"
" }] \n";
std::ostringstream os;
// Create a valid config with all the parts should parse
os << config_set_txt << ","
<< args_txt
<< dhcp4_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket
<< logger_txt
<< "}\n" // close dhcp4
<< "}}";
// Send the config-set command
std::string response;
sendHttpCommand(os.str(), response);
EXPECT_EQ("[ { \"arguments\": { \"hash\": \"F6137301FF10D81585E041FD5FD8E91347ACADDE64F92ED03432FB100874DE02\" }, \"result\": 0, \"text\": \"Configuration successful.\" } ]",
response);
// Check that the config was indeed applied.
const Subnet4Collection* subnets =
CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
EXPECT_EQ(1, subnets->size());
OptionDefinitionPtr def =
LibDHCP::getRuntimeOptionDef(DHCP4_OPTION_SPACE, 163);
ASSERT_TRUE(def);
// Verify the HTTP control channel socket exists.
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
// Send the config-set command.
sendHttpCommand(os.str(), response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
EXPECT_EQ("[ { \"arguments\": { \"hash\": \"F6137301FF10D81585E041FD5FD8E91347ACADDE64F92ED03432FB100874DE02\" }, \"result\": 0, \"text\": \"Configuration successful.\" } ]",
response);
// Check that the config was not lost
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
EXPECT_EQ(1, subnets->size());
// Clean up after the test.
CfgMgr::instance().clear();
}
// Verify that the "config-set" command will reuse listener
TEST_F(HttpsCtrlChannelDhcpv4Test, noListenerChange) {
createHttpChannelServer();
// Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find)
string ca_dir(string(TEST_CA_DIR));
string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n";
string dhcp4_cfg_txt =
" \"Dhcp4\": { \n"
" \"interfaces-config\": { \n"
" \"interfaces\": [\"*\"] \n"
" }, \n"
" \"valid-lifetime\": 4000, \n"
" \"renew-timer\": 1000, \n"
" \"rebind-timer\": 2000, \n"
" \"lease-database\": { \n"
" \"type\": \"memfile\", \n"
" \"persist\":false, \n"
" \"lfc-interval\": 0 \n"
" }, \n"
" \"expired-leases-processing\": { \n"
" \"reclaim-timer-wait-time\": 0, \n"
" \"hold-reclaimed-time\": 0, \n"
" \"flush-reclaimed-timer-wait-time\": 0 \n"
" },"
" \"subnet4\": [ \n";
string subnet1 =
" {\"subnet\": \"192.2.0.0/24\", \"id\": 1, \n"
" \"pools\": [{ \"pool\": \"192.2.0.1-192.2.0.50\" }]}\n";
string subnet_footer =
" ] \n";
string option_def =
" ,\"option-def\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"type\": \"uint32\",\n"
" \"array\": false,\n"
" \"record-types\": \"\",\n"
" \"space\": \"dhcp4\",\n"
" \"encapsulate\": \"\"\n"
" }\n"
"]\n";
string option_data =
" ,\"option-data\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"space\": \"dhcp4\",\n"
" \"csv-format\": true,\n"
" \"data\": \"12345\"\n"
" }\n"
"]\n";
string control_socket_header =
" ,\"control-socket\": { \n";
string control_socket_footer =
" \"socket-type\": \"http\", \n"
" \"socket-address\": \"127.0.0.1\", \n"
" \"socket-port\": 18124 \n"
" } \n";
string logger_txt =
" ,\"loggers\": [ { \n"
" \"name\": \"kea\", \n"
" \"severity\": \"FATAL\", \n"
" \"output-options\": [{ \n"
" \"output\": \"/dev/null\", \n"
" \"maxsize\": 0"
" }] \n"
" }] \n";
std::ostringstream os;
// Create a valid config with all the parts should parse
os << config_set_txt << ","
<< args_txt
<< dhcp4_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket_header
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\", \n"
<< control_socket_footer
<< logger_txt
<< "}\n" // close dhcp4
<< "}}";
// Send the config-set command
std::string response;
sendHttpCommand(os.str(), response);
// Verify the configuration was successful. The config contains random
// file paths (CA directory), so the hash will be different each time.
// As such, we can do simplified checks:
// - verify the "result": 0 is there
// - verify the "text": "Configuration successful." is there
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration successful.\""),
std::string::npos);
// Check that the config was indeed applied.
const Subnet4Collection* subnets =
CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
EXPECT_EQ(1, subnets->size());
OptionDefinitionPtr def =
LibDHCP::getRuntimeOptionDef(DHCP4_OPTION_SPACE, 163);
ASSERT_TRUE(def);
// Verify the HTTP control channel socket exists.
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
auto const context = HttpCommandMgr::instance().getHttpListener()->getTlsContext().get();
// Send the config-set command.
sendHttpCommand(os.str(), response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
// The TLS settings have been applied
EXPECT_NE(context, HttpCommandMgr::instance().getHttpListener()->getTlsContext().get());
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration successful.\""),
std::string::npos);
// Check that the config was not lost
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
EXPECT_EQ(1, subnets->size());
// Clean up after the test.
CfgMgr::instance().clear();
}
// Verify that the "config-set" command will reuse listener
TEST_F(HttpCtrlChannelDhcpv4Test, ignoredHttpToHttpsSwitch) {
createHttpChannelServer();
// Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find)
string ca_dir(string(TEST_CA_DIR));
string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n";
string dhcp4_cfg_txt =
" \"Dhcp4\": { \n"
" \"interfaces-config\": { \n"
" \"interfaces\": [\"*\"] \n"
" }, \n"
" \"valid-lifetime\": 4000, \n"
" \"renew-timer\": 1000, \n"
" \"rebind-timer\": 2000, \n"
" \"lease-database\": { \n"
" \"type\": \"memfile\", \n"
" \"persist\":false, \n"
" \"lfc-interval\": 0 \n"
" }, \n"
" \"expired-leases-processing\": { \n"
" \"reclaim-timer-wait-time\": 0, \n"
" \"hold-reclaimed-time\": 0, \n"
" \"flush-reclaimed-timer-wait-time\": 0 \n"
" },"
" \"subnet4\": [ \n";
string subnet1 =
" {\"subnet\": \"192.2.0.0/24\", \"id\": 1, \n"
" \"pools\": [{ \"pool\": \"192.2.0.1-192.2.0.50\" }]}\n";
string subnet_footer =
" ] \n";
string option_def =
" ,\"option-def\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"type\": \"uint32\",\n"
" \"array\": false,\n"
" \"record-types\": \"\",\n"
" \"space\": \"dhcp4\",\n"
" \"encapsulate\": \"\"\n"
" }\n"
"]\n";
string option_data =
" ,\"option-data\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"space\": \"dhcp4\",\n"
" \"csv-format\": true,\n"
" \"data\": \"12345\"\n"
" }\n"
"]\n";
string control_socket_header =
" ,\"control-socket\": { \n";
string control_socket_footer =
" \"socket-type\": \"http\", \n"
" \"socket-address\": \"127.0.0.1\", \n"
" \"socket-port\": 18124 \n"
" } \n";
string logger_txt =
" ,\"loggers\": [ { \n"
" \"name\": \"kea\", \n"
" \"severity\": \"FATAL\", \n"
" \"output-options\": [{ \n"
" \"output\": \"/dev/null\", \n"
" \"maxsize\": 0"
" }] \n"
" }] \n";
std::ostringstream os;
// Create a valid config with all the parts should parse
os << config_set_txt << ","
<< args_txt
<< dhcp4_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket_header
<< control_socket_footer
<< logger_txt
<< "}\n" // close dhcp4
<< "}}";
// Send the config-set command
std::string response;
sendHttpCommand(os.str(), response);
EXPECT_EQ("[ { \"arguments\": { \"hash\": \"F6137301FF10D81585E041FD5FD8E91347ACADDE64F92ED03432FB100874DE02\" }, \"result\": 0, \"text\": \"Configuration successful.\" } ]",
response);
// Check that the config was indeed applied.
const Subnet4Collection* subnets =
CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
EXPECT_EQ(1, subnets->size());
OptionDefinitionPtr def =
LibDHCP::getRuntimeOptionDef(DHCP4_OPTION_SPACE, 163);
ASSERT_TRUE(def);
// Verify the HTTP control channel socket exists.
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
std::ostringstream second_config_os;
// Create a valid config with all the parts should parse
second_config_os << config_set_txt << ","
<< args_txt
<< dhcp4_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket_header
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\", \n"
<< control_socket_footer
<< logger_txt
<< "}\n" // close dhcp4
<< "}}";
// Send the config-set command.
sendHttpCommand(second_config_os.str(), response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration successful.\""),
std::string::npos);
// Check that the config was not lost
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
EXPECT_EQ(1, subnets->size());
// Clean up after the test.
CfgMgr::instance().clear();
}
// Verify that the "config-set" command will reuse listener
TEST_F(HttpsCtrlChannelDhcpv4Test, ignoreHttpsToHttpSwitch) {
createHttpChannelServer();
// Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find)
string ca_dir(string(TEST_CA_DIR));
string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n";
string dhcp4_cfg_txt =
" \"Dhcp4\": { \n"
" \"interfaces-config\": { \n"
" \"interfaces\": [\"*\"] \n"
" }, \n"
" \"valid-lifetime\": 4000, \n"
" \"renew-timer\": 1000, \n"
" \"rebind-timer\": 2000, \n"
" \"lease-database\": { \n"
" \"type\": \"memfile\", \n"
" \"persist\":false, \n"
" \"lfc-interval\": 0 \n"
" }, \n"
" \"expired-leases-processing\": { \n"
" \"reclaim-timer-wait-time\": 0, \n"
" \"hold-reclaimed-time\": 0, \n"
" \"flush-reclaimed-timer-wait-time\": 0 \n"
" },"
" \"subnet4\": [ \n";
string subnet1 =
" {\"subnet\": \"192.2.0.0/24\", \"id\": 1, \n"
" \"pools\": [{ \"pool\": \"192.2.0.1-192.2.0.50\" }]}\n";
string subnet_footer =
" ] \n";
string option_def =
" ,\"option-def\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"type\": \"uint32\",\n"
" \"array\": false,\n"
" \"record-types\": \"\",\n"
" \"space\": \"dhcp4\",\n"
" \"encapsulate\": \"\"\n"
" }\n"
"]\n";
string option_data =
" ,\"option-data\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"space\": \"dhcp4\",\n"
" \"csv-format\": true,\n"
" \"data\": \"12345\"\n"
" }\n"
"]\n";
string control_socket_header =
" ,\"control-socket\": { \n";
string control_socket_footer =
" \"socket-type\": \"http\", \n"
" \"socket-address\": \"127.0.0.1\", \n"
" \"socket-port\": 18124 \n"
" } \n";
string logger_txt =
" ,\"loggers\": [ { \n"
" \"name\": \"kea\", \n"
" \"severity\": \"FATAL\", \n"
" \"output-options\": [{ \n"
" \"output\": \"/dev/null\", \n"
" \"maxsize\": 0"
" }] \n"
" }] \n";
std::ostringstream os;
// Create a valid config with all the parts should parse
os << config_set_txt << ","
<< args_txt
<< dhcp4_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket_header
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\", \n"
<< control_socket_footer
<< logger_txt
<< "}\n" // close dhcp4
<< "}}";
// Send the config-set command
std::string response;
sendHttpCommand(os.str(), response);
// Verify the configuration was successful. The config contains random
// file paths (CA directory), so the hash will be different each time.
// As such, we can do simplified checks:
// - verify the "result": 0 is there
// - verify the "text": "Configuration successful." is there
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration successful.\""),
std::string::npos);
// Check that the config was indeed applied.
const Subnet4Collection* subnets =
CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
EXPECT_EQ(1, subnets->size());
OptionDefinitionPtr def =
LibDHCP::getRuntimeOptionDef(DHCP4_OPTION_SPACE, 163);
ASSERT_TRUE(def);
// Verify the HTTP control channel socket exists.
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
// The TLS settings have not changed
auto const context = HttpCommandMgr::instance().getHttpListener()->getTlsContext().get();
std::ostringstream second_config_os;
// Create a valid config with all the parts should parse
second_config_os << config_set_txt << ","
<< args_txt
<< dhcp4_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket_header
<< control_socket_footer
<< logger_txt
<< "}\n" // close dhcp4
<< "}}";
// Send the config-set command.
sendHttpCommand(second_config_os.str(), response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
EXPECT_EQ(context, HttpCommandMgr::instance().getHttpListener()->getTlsContext().get());
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration successful.\""),
std::string::npos);
// Check that the config was not lost
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
EXPECT_EQ(1, subnets->size());
// Clean up after the test.
CfgMgr::instance().clear();
}
} // End of anonymous namespace } // End of anonymous namespace

View File

@@ -440,15 +440,10 @@ void configureCommandChannel() {
ConstElementPtr current_http_config = ConstElementPtr current_http_config =
CfgMgr::instance().getCurrentCfg()->getHttpControlSocketInfo(); CfgMgr::instance().getCurrentCfg()->getHttpControlSocketInfo();
sock_changed = (http_config && current_http_config && if (http_config) {
!http_config->equals(*current_http_config)); HttpCommandMgr::instance().openCommandSockets(http_config);
} else if (current_http_config) {
if (!http_config || !current_http_config || sock_changed) { HttpCommandMgr::instance().closeCommandSockets();
if (http_config) {
HttpCommandMgr::instance().openCommandSockets(http_config);
} else if (current_http_config) {
HttpCommandMgr::instance().closeCommandSockets();
}
} }
} }

View File

@@ -1013,7 +1013,7 @@ TEST_F(HttpCtrlChannelDhcpv6Test, configSet) {
// Define strings to permutate the config arguments // Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find) // (Note the line feeds makes errors easy to find)
string set_config_txt = "{ \"command\": \"config-set\" \n"; string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n"; string args_txt = " \"arguments\": { \n";
string dhcp6_cfg_txt = string dhcp6_cfg_txt =
" \"Dhcp6\": { \n" " \"Dhcp6\": { \n"
@@ -1087,7 +1087,7 @@ TEST_F(HttpCtrlChannelDhcpv6Test, configSet) {
std::ostringstream os; std::ostringstream os;
// Create a valid config with all the parts should parse // Create a valid config with all the parts should parse
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp6_cfg_txt << dhcp6_cfg_txt
<< subnet1 << subnet1
@@ -1116,7 +1116,7 @@ TEST_F(HttpCtrlChannelDhcpv6Test, configSet) {
// Create a config with malformed subnet that should fail to parse. // Create a config with malformed subnet that should fail to parse.
os.str(""); os.str("");
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp6_cfg_txt << dhcp6_cfg_txt
<< bad_subnet << bad_subnet
@@ -1145,7 +1145,7 @@ TEST_F(HttpCtrlChannelDhcpv6Test, configSet) {
// Create a valid config with two subnets and no command channel. // Create a valid config with two subnets and no command channel.
// It should succeed, client should still receive the response // It should succeed, client should still receive the response
os.str(""); os.str("");
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp6_cfg_txt << dhcp6_cfg_txt
<< subnet1 << subnet1
@@ -1184,7 +1184,7 @@ TEST_F(HttpsCtrlChannelDhcpv6Test, configSet) {
// Define strings to permutate the config arguments // Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find) // (Note the line feeds makes errors easy to find)
string ca_dir(string(TEST_CA_DIR)); string ca_dir(string(TEST_CA_DIR));
string set_config_txt = "{ \"command\": \"config-set\" \n"; string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n"; string args_txt = " \"arguments\": { \n";
string dhcp6_cfg_txt = string dhcp6_cfg_txt =
" \"Dhcp6\": { \n" " \"Dhcp6\": { \n"
@@ -1259,7 +1259,7 @@ TEST_F(HttpsCtrlChannelDhcpv6Test, configSet) {
std::ostringstream os; std::ostringstream os;
// Create a valid config with all the parts should parse // Create a valid config with all the parts should parse
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp6_cfg_txt << dhcp6_cfg_txt
<< subnet1 << subnet1
@@ -1298,7 +1298,7 @@ TEST_F(HttpsCtrlChannelDhcpv6Test, configSet) {
// Create a config with malformed subnet that should fail to parse. // Create a config with malformed subnet that should fail to parse.
os.str(""); os.str("");
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp6_cfg_txt << dhcp6_cfg_txt
<< bad_subnet << bad_subnet
@@ -1331,7 +1331,7 @@ TEST_F(HttpsCtrlChannelDhcpv6Test, configSet) {
// Create a valid config with two subnets and no command channel. // Create a valid config with two subnets and no command channel.
// It should succeed, client should still receive the response // It should succeed, client should still receive the response
os.str(""); os.str("");
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp6_cfg_txt << dhcp6_cfg_txt
<< subnet1 << subnet1
@@ -1443,7 +1443,7 @@ TEST_F(HttpCtrlChannelDhcpv6Test, configTest) {
// Define strings to permutate the config arguments // Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find) // (Note the line feeds makes errors easy to find)
string set_config_txt = "{ \"command\": \"config-set\" \n"; string config_set_txt = "{ \"command\": \"config-set\" \n";
string config_test_txt = "{ \"command\": \"config-test\" \n"; string config_test_txt = "{ \"command\": \"config-test\" \n";
string args_txt = " \"arguments\": { \n"; string args_txt = " \"arguments\": { \n";
string dhcp6_cfg_txt = string dhcp6_cfg_txt =
@@ -1496,7 +1496,7 @@ TEST_F(HttpCtrlChannelDhcpv6Test, configTest) {
std::ostringstream os; std::ostringstream os;
// Create a valid config with all the parts should parse // Create a valid config with all the parts should parse
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp6_cfg_txt << dhcp6_cfg_txt
<< subnet1 << subnet1
@@ -1585,7 +1585,7 @@ TEST_F(HttpsCtrlChannelDhcpv6Test, configTest) {
// Define strings to permutate the config arguments // Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find) // (Note the line feeds makes errors easy to find)
string ca_dir(string(TEST_CA_DIR)); string ca_dir(string(TEST_CA_DIR));
string set_config_txt = "{ \"command\": \"config-set\" \n"; string config_set_txt = "{ \"command\": \"config-set\" \n";
string config_test_txt = "{ \"command\": \"config-test\" \n"; string config_test_txt = "{ \"command\": \"config-test\" \n";
string args_txt = " \"arguments\": { \n"; string args_txt = " \"arguments\": { \n";
string dhcp6_cfg_txt = string dhcp6_cfg_txt =
@@ -1639,7 +1639,7 @@ TEST_F(HttpsCtrlChannelDhcpv6Test, configTest) {
std::ostringstream os; std::ostringstream os;
// Create a valid config with all the parts should parse // Create a valid config with all the parts should parse
os << set_config_txt << "," os << config_set_txt << ","
<< args_txt << args_txt
<< dhcp6_cfg_txt << dhcp6_cfg_txt
<< subnet1 << subnet1
@@ -3401,4 +3401,571 @@ TEST_F(HttpsCtrlChannelDhcpv6Test, connectionTimeoutNoData) {
testConnectionTimeoutNoData(); testConnectionTimeoutNoData();
} }
// Verify that the "config-set" command will reuse listener
TEST_F(HttpCtrlChannelDhcpv6Test, noListenerChange) {
createHttpChannelServer();
// Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find)
string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n";
string dhcp6_cfg_txt =
" \"Dhcp6\": { \n"
" \"interfaces-config\": { \n"
" \"interfaces\": [\"*\"] \n"
" }, \n"
" \"preferred-lifetime\": 3000, \n"
" \"valid-lifetime\": 4000, \n"
" \"renew-timer\": 1000, \n"
" \"rebind-timer\": 2000, \n"
" \"lease-database\": { \n"
" \"type\": \"memfile\", \n"
" \"persist\":false, \n"
" \"lfc-interval\": 0 \n"
" }, \n"
" \"expired-leases-processing\": { \n"
" \"reclaim-timer-wait-time\": 0, \n"
" \"hold-reclaimed-time\": 0, \n"
" \"flush-reclaimed-timer-wait-time\": 0 \n"
" },"
" \"subnet6\": [ \n";
string subnet1 =
" {\"subnet\": \"3002::/64\", \"id\": 1, \n"
" \"pools\": [{ \"pool\": \"3002::100-3002::200\" }]}\n";
string subnet_footer =
" ] \n";
string option_def =
" ,\"option-def\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"type\": \"uint32\",\n"
" \"array\": false,\n"
" \"record-types\": \"\",\n"
" \"space\": \"dhcp6\",\n"
" \"encapsulate\": \"\"\n"
" }\n"
"]\n";
string option_data =
" ,\"option-data\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"space\": \"dhcp6\",\n"
" \"csv-format\": true,\n"
" \"data\": \"12345\"\n"
" }\n"
"]\n";
string control_socket =
" ,\"control-socket\": { \n"
" \"socket-type\": \"http\", \n"
" \"socket-address\": \"::1\", \n"
" \"socket-port\": 18126 \n"
" } \n";
string logger_txt =
" ,\"loggers\": [ { \n"
" \"name\": \"kea\", \n"
" \"severity\": \"FATAL\", \n"
" \"output-options\": [{ \n"
" \"output\": \"/dev/null\", \n"
" \"maxsize\": 0"
" }] \n"
" }] \n";
std::ostringstream os;
// Create a valid config with all the parts should parse
os << config_set_txt << ","
<< args_txt
<< dhcp6_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket
<< logger_txt
<< "}\n" // close dhcp6
<< "}}";
// Send the config-set command
std::string response;
sendHttpCommand(os.str(), response);
EXPECT_EQ("[ { \"arguments\": { \"hash\": \"BCE3D0CC68CBBB49C3F5967E3FFCB4E44E55CBFB53814761B12ADB5C7CD95C1F\" }, \"result\": 0, \"text\": \"Configuration successful.\" } ]",
response);
// Check that the config was indeed applied.
const Subnet6Collection* subnets =
CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
EXPECT_EQ(1, subnets->size());
OptionDefinitionPtr def =
LibDHCP::getRuntimeOptionDef(DHCP6_OPTION_SPACE, 163);
ASSERT_TRUE(def);
// Verify the HTTP control channel socket exists.
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
// Send the config-set command.
sendHttpCommand(os.str(), response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
EXPECT_EQ("[ { \"arguments\": { \"hash\": \"BCE3D0CC68CBBB49C3F5967E3FFCB4E44E55CBFB53814761B12ADB5C7CD95C1F\" }, \"result\": 0, \"text\": \"Configuration successful.\" } ]",
response);
// Check that the config was not lost
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
EXPECT_EQ(1, subnets->size());
// Clean up after the test.
CfgMgr::instance().clear();
}
// Verify that the "config-set" command will reuse listener
TEST_F(HttpsCtrlChannelDhcpv6Test, noListenerChange) {
createHttpChannelServer();
// Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find)
string ca_dir(string(TEST_CA_DIR));
string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n";
string dhcp6_cfg_txt =
" \"Dhcp6\": { \n"
" \"interfaces-config\": { \n"
" \"interfaces\": [\"*\"] \n"
" }, \n"
" \"preferred-lifetime\": 3000, \n"
" \"valid-lifetime\": 4000, \n"
" \"renew-timer\": 1000, \n"
" \"rebind-timer\": 2000, \n"
" \"lease-database\": { \n"
" \"type\": \"memfile\", \n"
" \"persist\":false, \n"
" \"lfc-interval\": 0 \n"
" }, \n"
" \"expired-leases-processing\": { \n"
" \"reclaim-timer-wait-time\": 0, \n"
" \"hold-reclaimed-time\": 0, \n"
" \"flush-reclaimed-timer-wait-time\": 0 \n"
" },"
" \"subnet6\": [ \n";
string subnet1 =
" {\"subnet\": \"3002::/64\", \"id\": 1, \n"
" \"pools\": [{ \"pool\": \"3002::100-3002::200\" }]}\n";
string subnet_footer =
" ] \n";
string option_def =
" ,\"option-def\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"type\": \"uint32\",\n"
" \"array\": false,\n"
" \"record-types\": \"\",\n"
" \"space\": \"dhcp6\",\n"
" \"encapsulate\": \"\"\n"
" }\n"
"]\n";
string option_data =
" ,\"option-data\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"space\": \"dhcp6\",\n"
" \"csv-format\": true,\n"
" \"data\": \"12345\"\n"
" }\n"
"]\n";
string control_socket_header =
" ,\"control-socket\": { \n";
string control_socket_footer =
" \"socket-type\": \"http\", \n"
" \"socket-address\": \"::1\", \n"
" \"socket-port\": 18126 \n"
" } \n";
string logger_txt =
" ,\"loggers\": [ { \n"
" \"name\": \"kea\", \n"
" \"severity\": \"FATAL\", \n"
" \"output-options\": [{ \n"
" \"output\": \"/dev/null\", \n"
" \"maxsize\": 0"
" }] \n"
" }] \n";
std::ostringstream os;
// Create a valid config with all the parts should parse
os << config_set_txt << ","
<< args_txt
<< dhcp6_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket_header
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\", \n"
<< control_socket_footer
<< logger_txt
<< "}\n" // close dhcp6
<< "}}";
// Send the config-set command
std::string response;
sendHttpCommand(os.str(), response);
// Verify the configuration was successful. The config contains random
// file paths (CA directory), so the hash will be different each time.
// As such, we can do simplified checks:
// - verify the "result": 0 is there
// - verify the "text": "Configuration successful." is there
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration successful.\""),
std::string::npos);
// Check that the config was indeed applied.
const Subnet6Collection* subnets =
CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
EXPECT_EQ(1, subnets->size());
OptionDefinitionPtr def =
LibDHCP::getRuntimeOptionDef(DHCP6_OPTION_SPACE, 163);
ASSERT_TRUE(def);
// Verify the HTTP control channel socket exists.
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
auto const context = HttpCommandMgr::instance().getHttpListener()->getTlsContext().get();
// Send the config-set command.
sendHttpCommand(os.str(), response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
// The TLS settings have been applied
EXPECT_NE(context, HttpCommandMgr::instance().getHttpListener()->getTlsContext().get());
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration successful.\""),
std::string::npos);
// Check that the config was not lost
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
EXPECT_EQ(1, subnets->size());
// Clean up after the test.
CfgMgr::instance().clear();
}
// Verify that the "config-set" command will reuse listener
TEST_F(HttpCtrlChannelDhcpv6Test, ignoreHttpToHttpsSwitch) {
createHttpChannelServer();
// Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find)
string ca_dir(string(TEST_CA_DIR));
string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n";
string dhcp6_cfg_txt =
" \"Dhcp6\": { \n"
" \"interfaces-config\": { \n"
" \"interfaces\": [\"*\"] \n"
" }, \n"
" \"preferred-lifetime\": 3000, \n"
" \"valid-lifetime\": 4000, \n"
" \"renew-timer\": 1000, \n"
" \"rebind-timer\": 2000, \n"
" \"lease-database\": { \n"
" \"type\": \"memfile\", \n"
" \"persist\":false, \n"
" \"lfc-interval\": 0 \n"
" }, \n"
" \"expired-leases-processing\": { \n"
" \"reclaim-timer-wait-time\": 0, \n"
" \"hold-reclaimed-time\": 0, \n"
" \"flush-reclaimed-timer-wait-time\": 0 \n"
" },"
" \"subnet6\": [ \n";
string subnet1 =
" {\"subnet\": \"3002::/64\", \"id\": 1, \n"
" \"pools\": [{ \"pool\": \"3002::100-3002::200\" }]}\n";
string subnet_footer =
" ] \n";
string option_def =
" ,\"option-def\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"type\": \"uint32\",\n"
" \"array\": false,\n"
" \"record-types\": \"\",\n"
" \"space\": \"dhcp6\",\n"
" \"encapsulate\": \"\"\n"
" }\n"
"]\n";
string option_data =
" ,\"option-data\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"space\": \"dhcp6\",\n"
" \"csv-format\": true,\n"
" \"data\": \"12345\"\n"
" }\n"
"]\n";
string control_socket_header =
" ,\"control-socket\": { \n";
string control_socket_footer =
" \"socket-type\": \"http\", \n"
" \"socket-address\": \"::1\", \n"
" \"socket-port\": 18126 \n"
" } \n";
string logger_txt =
" ,\"loggers\": [ { \n"
" \"name\": \"kea\", \n"
" \"severity\": \"FATAL\", \n"
" \"output-options\": [{ \n"
" \"output\": \"/dev/null\", \n"
" \"maxsize\": 0"
" }] \n"
" }] \n";
std::ostringstream os;
// Create a valid config with all the parts should parse
os << config_set_txt << ","
<< args_txt
<< dhcp6_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket_header
<< control_socket_footer
<< logger_txt
<< "}\n" // close dhcp6
<< "}}";
// Send the config-set command
std::string response;
sendHttpCommand(os.str(), response);
EXPECT_EQ("[ { \"arguments\": { \"hash\": \"BCE3D0CC68CBBB49C3F5967E3FFCB4E44E55CBFB53814761B12ADB5C7CD95C1F\" }, \"result\": 0, \"text\": \"Configuration successful.\" } ]",
response);
// Check that the config was indeed applied.
const Subnet6Collection* subnets =
CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
EXPECT_EQ(1, subnets->size());
OptionDefinitionPtr def =
LibDHCP::getRuntimeOptionDef(DHCP6_OPTION_SPACE, 163);
ASSERT_TRUE(def);
// Verify the HTTP control channel socket exists.
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
std::ostringstream second_config_os;
// Create a valid config with all the parts should parse
second_config_os << config_set_txt << ","
<< args_txt
<< dhcp6_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket_header
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\", \n"
<< control_socket_footer
<< logger_txt
<< "}\n" // close dhcp6
<< "}}";
// Send the config-set command.
sendHttpCommand(second_config_os.str(), response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_FALSE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration successful.\""),
std::string::npos);
// Check that the config was not lost
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
EXPECT_EQ(1, subnets->size());
// Clean up after the test.
CfgMgr::instance().clear();
}
// Verify that the "config-set" command will reuse listener
TEST_F(HttpsCtrlChannelDhcpv6Test, ignoreHttpsToHttpSwitch) {
createHttpChannelServer();
// Define strings to permutate the config arguments
// (Note the line feeds makes errors easy to find)
string ca_dir(string(TEST_CA_DIR));
string config_set_txt = "{ \"command\": \"config-set\" \n";
string args_txt = " \"arguments\": { \n";
string dhcp6_cfg_txt =
" \"Dhcp6\": { \n"
" \"interfaces-config\": { \n"
" \"interfaces\": [\"*\"] \n"
" }, \n"
" \"preferred-lifetime\": 3000, \n"
" \"valid-lifetime\": 4000, \n"
" \"renew-timer\": 1000, \n"
" \"rebind-timer\": 2000, \n"
" \"lease-database\": { \n"
" \"type\": \"memfile\", \n"
" \"persist\":false, \n"
" \"lfc-interval\": 0 \n"
" }, \n"
" \"expired-leases-processing\": { \n"
" \"reclaim-timer-wait-time\": 0, \n"
" \"hold-reclaimed-time\": 0, \n"
" \"flush-reclaimed-timer-wait-time\": 0 \n"
" },"
" \"subnet6\": [ \n";
string subnet1 =
" {\"subnet\": \"3002::/64\", \"id\": 1, \n"
" \"pools\": [{ \"pool\": \"3002::100-3002::200\" }]}\n";
string subnet_footer =
" ] \n";
string option_def =
" ,\"option-def\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"type\": \"uint32\",\n"
" \"array\": false,\n"
" \"record-types\": \"\",\n"
" \"space\": \"dhcp6\",\n"
" \"encapsulate\": \"\"\n"
" }\n"
"]\n";
string option_data =
" ,\"option-data\": [\n"
" {\n"
" \"name\": \"foo\",\n"
" \"code\": 163,\n"
" \"space\": \"dhcp6\",\n"
" \"csv-format\": true,\n"
" \"data\": \"12345\"\n"
" }\n"
"]\n";
string control_socket_header =
" ,\"control-socket\": { \n";
string control_socket_footer =
" \"socket-type\": \"http\", \n"
" \"socket-address\": \"::1\", \n"
" \"socket-port\": 18126 \n"
" } \n";
string logger_txt =
" ,\"loggers\": [ { \n"
" \"name\": \"kea\", \n"
" \"severity\": \"FATAL\", \n"
" \"output-options\": [{ \n"
" \"output\": \"/dev/null\", \n"
" \"maxsize\": 0"
" }] \n"
" }] \n";
std::ostringstream os;
// Create a valid config with all the parts should parse
os << config_set_txt << ","
<< args_txt
<< dhcp6_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket_header
<< " \"trust-anchor\": \"" << ca_dir << "/kea-ca.crt\", \n"
<< " \"cert-file\": \"" << ca_dir << "/kea-server.crt\", \n"
<< " \"key-file\": \"" << ca_dir << "/kea-server.key\", \n"
<< control_socket_footer
<< logger_txt
<< "}\n" // close dhcp6
<< "}}";
// Send the config-set command
std::string response;
sendHttpCommand(os.str(), response);
// Verify the configuration was successful. The config contains random
// file paths (CA directory), so the hash will be different each time.
// As such, we can do simplified checks:
// - verify the "result": 0 is there
// - verify the "text": "Configuration successful." is there
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration successful.\""),
std::string::npos);
// Check that the config was indeed applied.
const Subnet6Collection* subnets =
CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
EXPECT_EQ(1, subnets->size());
OptionDefinitionPtr def =
LibDHCP::getRuntimeOptionDef(DHCP6_OPTION_SPACE, 163);
ASSERT_TRUE(def);
// Verify the HTTP control channel socket exists.
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
auto const listener = HttpCommandMgr::instance().getHttpListener().get();
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
// The TLS settings have not changed
auto const context = HttpCommandMgr::instance().getHttpListener()->getTlsContext().get();
std::ostringstream second_config_os;
// Create a valid config with all the parts should parse
second_config_os << config_set_txt << ","
<< args_txt
<< dhcp6_cfg_txt
<< subnet1
<< subnet_footer
<< option_def
<< option_data
<< control_socket_header
<< control_socket_footer
<< logger_txt
<< "}\n" // close dhcp6
<< "}}";
// Send the config-set command.
sendHttpCommand(second_config_os.str(), response);
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener());
EXPECT_EQ(listener, HttpCommandMgr::instance().getHttpListener().get());
ASSERT_TRUE(HttpCommandMgr::instance().getHttpListener()->getTlsContext());
EXPECT_EQ(context, HttpCommandMgr::instance().getHttpListener()->getTlsContext().get());
EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
EXPECT_NE(response.find("\"text\": \"Configuration successful.\""),
std::string::npos);
// Check that the config was not lost
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
EXPECT_EQ(1, subnets->size());
// Clean up after the test.
CfgMgr::instance().clear();
}
} // End of anonymous namespace } // End of anonymous namespace

View File

@@ -34,7 +34,8 @@ extern const isc::log::MessageID COMMAND_SOCKET_WRITE_FAIL = "COMMAND_SOCKET_WRI
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLEAR_ERROR = "COMMAND_WATCH_SOCKET_CLEAR_ERROR"; extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLEAR_ERROR = "COMMAND_WATCH_SOCKET_CLEAR_ERROR";
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLOSE_ERROR = "COMMAND_WATCH_SOCKET_CLOSE_ERROR"; extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLOSE_ERROR = "COMMAND_WATCH_SOCKET_CLOSE_ERROR";
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_MARK_READY_ERROR = "COMMAND_WATCH_SOCKET_MARK_READY_ERROR"; extern const isc::log::MessageID COMMAND_WATCH_SOCKET_MARK_READY_ERROR = "COMMAND_WATCH_SOCKET_MARK_READY_ERROR";
extern const isc::log::MessageID HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES = "HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES"; extern const isc::log::MessageID HTTP_COMMAND_MGR_HTTPS_SERVICE_REUSED = "HTTP_COMMAND_MGR_HTTPS_SERVICE_REUSED";
extern const isc::log::MessageID HTTP_COMMAND_MGR_HTTP_SERVICE_REUSED = "HTTP_COMMAND_MGR_HTTP_SERVICE_REUSED";
extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STARTED = "HTTP_COMMAND_MGR_SERVICE_STARTED"; extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STARTED = "HTTP_COMMAND_MGR_SERVICE_STARTED";
extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STOPPING = "HTTP_COMMAND_MGR_SERVICE_STOPPING"; extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STOPPING = "HTTP_COMMAND_MGR_SERVICE_STOPPING";
extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STOPPING_ALL = "HTTP_COMMAND_MGR_SERVICE_STOPPING_ALL"; extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STOPPING_ALL = "HTTP_COMMAND_MGR_SERVICE_STOPPING_ALL";
@@ -73,7 +74,8 @@ const char* values[] = {
"COMMAND_WATCH_SOCKET_CLEAR_ERROR", "watch socket failed to clear: %1", "COMMAND_WATCH_SOCKET_CLEAR_ERROR", "watch socket failed to clear: %1",
"COMMAND_WATCH_SOCKET_CLOSE_ERROR", "watch socket failed to close: %1", "COMMAND_WATCH_SOCKET_CLOSE_ERROR", "watch socket failed to close: %1",
"COMMAND_WATCH_SOCKET_MARK_READY_ERROR", "watch socket failed to mark ready: %1", "COMMAND_WATCH_SOCKET_MARK_READY_ERROR", "watch socket failed to mark ready: %1",
"HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES", "ignore a change in TLS setup of the http control socket", "HTTP_COMMAND_MGR_HTTPS_SERVICE_REUSED", "reused HTTPS service bound to address %1:%2",
"HTTP_COMMAND_MGR_HTTP_SERVICE_REUSED", "reused HTTP service bound to address %1:%2",
"HTTP_COMMAND_MGR_SERVICE_STARTED", "started %1 service bound to address %2 port %3", "HTTP_COMMAND_MGR_SERVICE_STARTED", "started %1 service bound to address %2 port %3",
"HTTP_COMMAND_MGR_SERVICE_STOPPING", "Server is stopping %1 service %2", "HTTP_COMMAND_MGR_SERVICE_STOPPING", "Server is stopping %1 service %2",
"HTTP_COMMAND_MGR_SERVICE_STOPPING_ALL", "stopping %1 service %2", "HTTP_COMMAND_MGR_SERVICE_STOPPING_ALL", "stopping %1 service %2",

View File

@@ -35,7 +35,8 @@ extern const isc::log::MessageID COMMAND_SOCKET_WRITE_FAIL;
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLEAR_ERROR; extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLEAR_ERROR;
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLOSE_ERROR; extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLOSE_ERROR;
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_MARK_READY_ERROR; extern const isc::log::MessageID COMMAND_WATCH_SOCKET_MARK_READY_ERROR;
extern const isc::log::MessageID HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES; extern const isc::log::MessageID HTTP_COMMAND_MGR_HTTPS_SERVICE_REUSED;
extern const isc::log::MessageID HTTP_COMMAND_MGR_HTTP_SERVICE_REUSED;
extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STARTED; extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STARTED;
extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STOPPING; extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STOPPING;
extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STOPPING_ALL; extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STOPPING_ALL;

View File

@@ -153,11 +153,14 @@ ready status after scheduling asynchronous send. This is programmatic error
that should be reported. The command manager may or may not continue that should be reported. The command manager may or may not continue
to operate correctly. to operate correctly.
% HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES ignore a change in TLS setup of the http control socket % HTTP_COMMAND_MGR_HTTPS_SERVICE_REUSED reused HTTPS service bound to address %1:%2
The warning message is issued when the HTTP/HTTPS control socket was This informational message indicates that the server has reused existing
reconfigured with a different TLS setup but keeping the address and port. HTTPS service on the specified address and port. Note that any change in
These changes are ignored because they can't be applied without opening a new the TLS setup was ignored.
socket which will conflict with the existing one.
% HTTP_COMMAND_MGR_HTTP_SERVICE_REUSED reused HTTP service bound to address %1:%2
This informational message indicates that the server has reused existing
HTTP service on the specified address and port.
% HTTP_COMMAND_MGR_SERVICE_STARTED started %1 service bound to address %2 port %3 % HTTP_COMMAND_MGR_SERVICE_STARTED started %1 service bound to address %2 port %3
This informational message indicates that the server has started This informational message indicates that the server has started

View File

@@ -127,15 +127,37 @@ HttpCommandMgrImpl::openCommandSocket(const isc::data::ConstElementPtr config) {
// Search for the specific connection and reuse the existing one if found. // Search for the specific connection and reuse the existing one if found.
auto it = sockets_.find(std::make_pair(server_address, server_port)); auto it = sockets_.find(std::make_pair(server_address, server_port));
if (it != sockets_.end()) { if (it != sockets_.end()) {
if ((cmd_config->getTrustAnchor() != it->second->config_->getTrustAnchor()) || auto listener = it->second->listener_;
(cmd_config->getCertFile() != it->second->config_->getCertFile()) || if (listener) {
(cmd_config->getKeyFile() != it->second->config_->getKeyFile()) || // Reconfig keeping the same address and port.
(cmd_config->getCertRequired() != it->second->config_->getCertRequired())) { if (listener->getTlsContext()) {
LOG_WARN(command_logger, HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES); if (cmd_config->getTrustAnchor().empty()) {
// Overwrite the authentication setup and the emulation flag // Can not switch from HTTPS to HTTP
// in the response creator config. LOG_INFO(command_logger, HTTP_COMMAND_MGR_HTTPS_SERVICE_REUSED)
it->second->config_->setAuthConfig(cmd_config->getAuthConfig()); .arg(server_address.toText())
it->second->config_->setEmulateAgentResponse(cmd_config->getEmulateAgentResponse()); .arg(server_port);
} else {
// Apply TLS settings each time.
TlsContextPtr tls_context;
TlsContext::configure(tls_context,
TlsRole::SERVER,
cmd_config->getTrustAnchor(),
cmd_config->getCertFile(),
cmd_config->getKeyFile(),
cmd_config->getCertRequired());
// Overwrite the authentication setup, the http headers and the emulation flag
// in the response creator config.
it->second->config_->setAuthConfig(cmd_config->getAuthConfig());
it->second->config_->setHttpHeaders(cmd_config->getHttpHeaders());
it->second->config_->setEmulateAgentResponse(cmd_config->getEmulateAgentResponse());
io_service_->post([listener, tls_context]() { listener->setTlsContext(tls_context); });
}
} else if (!cmd_config->getTrustAnchor().empty()) {
// Can not switch from HTTP to HTTPS
LOG_INFO(command_logger, HTTP_COMMAND_MGR_HTTP_SERVICE_REUSED)
.arg(server_address.toText())
.arg(server_port);
}
} }
// If the connection can be reused, mark it as usable. // If the connection can be reused, mark it as usable.
it->second->usable_ = true; it->second->usable_ = true;

View File

@@ -47,6 +47,11 @@ HttpListener::getTlsContext() const {
return (impl_->getTlsContext()); return (impl_->getTlsContext());
} }
void
HttpListener::setTlsContext(const TlsContextPtr& context) {
impl_->setTlsContext(context);
}
int int
HttpListener::getNative() const { HttpListener::getNative() const {
return (impl_->getNative()); return (impl_->getNative());

View File

@@ -118,6 +118,9 @@ public:
/// @brief Returns reference to the current TLS context. /// @brief Returns reference to the current TLS context.
const asiolink::TlsContextPtr& getTlsContext() const; const asiolink::TlsContextPtr& getTlsContext() const;
/// @brief Sets reference of the current TLS context.
void setTlsContext(const asiolink::TlsContextPtr& context);
/// @brief file descriptor of the underlying acceptor socket. /// @brief file descriptor of the underlying acceptor socket.
int getNative() const; int getNative() const;

View File

@@ -75,6 +75,11 @@ HttpListenerImpl::getTlsContext() const {
return (tls_context_); return (tls_context_);
} }
void
HttpListenerImpl::setTlsContext(const TlsContextPtr& context) {
tls_context_ = context;
}
int int
HttpListenerImpl::getNative() const { HttpListenerImpl::getNative() const {
return (acceptor_ ? acceptor_->getNative() : -1); return (acceptor_ ? acceptor_->getNative() : -1);

View File

@@ -63,6 +63,9 @@ public:
/// @brief Returns reference to the current TLS context. /// @brief Returns reference to the current TLS context.
const asiolink::TlsContextPtr& getTlsContext() const; const asiolink::TlsContextPtr& getTlsContext() const;
/// @brief Sets reference of the current TLS context.
void setTlsContext(const asiolink::TlsContextPtr& context);
/// @brief file descriptor of the underlying acceptor socket. /// @brief file descriptor of the underlying acceptor socket.
int getNative() const; int getNative() const;