From: Francis Dupont Date: Wed, 1 Jun 2022 18:24:37 +0000 (+0200) Subject: [#1706] Added restrict-commands feature X-Git-Tag: Kea-2.1.7~43 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=80a567889a147180b956d225251e9e1d21aecdfd;p=thirdparty%2Fkea.git [#1706] Added restrict-commands feature --- diff --git a/doc/sphinx/arm/hooks-ha.rst b/doc/sphinx/arm/hooks-ha.rst index 7e77a7003f..3f19626b45 100644 --- a/doc/sphinx/arm/hooks-ha.rst +++ b/doc/sphinx/arm/hooks-ha.rst @@ -237,6 +237,10 @@ Since Kea 2.2.0 the HTTPS server side is supported: are required and verified, i.e. like ``cert-required``. It defaults to true and is a HA config (vs peer config) parameter. +Kea 2.2.0 adds to a new security feature with the ``restrict-commands`` +HA config parameter: when it is configured to ``true`` (default is +``false``) commands which are not used by the hook are rejected. + Following is an example of HA server pair and Control Agent configuration for Hot-Standby with TLS. diff --git a/src/hooks/dhcp/high_availability/command_creator.cc b/src/hooks/dhcp/high_availability/command_creator.cc index 0b481f540a..19af10b466 100644 --- a/src/hooks/dhcp/high_availability/command_creator.cc +++ b/src/hooks/dhcp/high_availability/command_creator.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -13,10 +13,26 @@ using namespace isc::data; using namespace isc::dhcp; +using namespace std; namespace isc { namespace ha { +unordered_set CommandCreator::ha_commands4_ = { + "list-commands", "status-get", + "dhcp-disable", "dhcp-enable", "ha-reset", "ha-heartbeat", + "lease4-update", "lease4-del", "lease4-get-all", "lease4-get-page", + "ha-maintenance-notify", "ha-sync-complete-notify" +}; + +unordered_set CommandCreator::ha_commands6_ = { + "list-commands", "status-get", + "dhcp-disable", "dhcp-enable", "ha-reset", "ha-heartbeat", + "lease6-bulk-apply", "lease6-update", "lease6-del", "lease6-get-all", + "lease6-get-page", + "ha-maintenance-notify", "ha-sync-complete-notify" +}; + ConstElementPtr CommandCreator::createDHCPDisable(const unsigned int max_period, const HAServerType& server_type) { @@ -248,7 +264,7 @@ void CommandCreator::insertService(ConstElementPtr& command, const HAServerType& server_type) { ElementPtr service = Element::createList(); - const std::string service_name = (server_type == HAServerType::DHCPv4 ? "dhcp4" : "dhcp6"); + const string service_name = (server_type == HAServerType::DHCPv4 ? "dhcp4" : "dhcp6"); service->add(Element::create(service_name)); // We have no better way of setting a new element here than diff --git a/src/hooks/dhcp/high_availability/command_creator.h b/src/hooks/dhcp/high_availability/command_creator.h index a65d575e35..276f405df5 100644 --- a/src/hooks/dhcp/high_availability/command_creator.h +++ b/src/hooks/dhcp/high_availability/command_creator.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace isc { @@ -172,6 +173,12 @@ public: static data::ConstElementPtr createSyncCompleteNotify(const HAServerType& server_type); + /// @brief List of commands used by the High Availability in v4. + static std::unordered_set ha_commands4_; + + /// @brief List of commands used by the High Availability in v6. + static std::unordered_set ha_commands6_; + private: /// @brief Replaces "cltt" with "expire" value within the lease. diff --git a/src/hooks/dhcp/high_availability/ha_config.cc b/src/hooks/dhcp/high_availability/ha_config.cc index 3d355412e4..1e3a8c684d 100644 --- a/src/hooks/dhcp/high_availability/ha_config.cc +++ b/src/hooks/dhcp/high_availability/ha_config.cc @@ -167,7 +167,8 @@ HAConfig::HAConfig() enable_multi_threading_(false), http_dedicated_listener_(false), http_listener_threads_(0), http_client_threads_(0), trust_anchor_(), cert_file_(), key_file_(), require_client_certs_(true), - peers_(), state_machine_(new StateMachineConfig()) { + restrict_commands_(false), peers_(), + state_machine_(new StateMachineConfig()) { } HAConfig::PeerConfigPtr diff --git a/src/hooks/dhcp/high_availability/ha_config.h b/src/hooks/dhcp/high_availability/ha_config.h index fd7710c7ad..bb4857777e 100644 --- a/src/hooks/dhcp/high_availability/ha_config.h +++ b/src/hooks/dhcp/high_availability/ha_config.h @@ -680,6 +680,18 @@ public: require_client_certs_ = flag; } + /// @brief Returns restrict-commands. + bool getRestrictCommands() const { + return (restrict_commands_); + } + + /// @brief Sets restrict-commands. + /// + /// @param flag Restrict commands to HA flag value. + void setRestrictCommands(bool flag) { + restrict_commands_ = flag; + } + /// @brief Returns configuration of the specified server. /// /// @param name Server name. @@ -765,6 +777,7 @@ public: util::Optional cert_file_; ///< Certificate file. util::Optional key_file_; ///< Private key file. bool require_client_certs_; ///< Require client certs flag. + bool restrict_commands_; ///< Restrict commands to HA flag. PeerConfigMap peers_; ///< Map of peers' configurations. StateMachineConfigPtr state_machine_; ///< State machine configuration. }; diff --git a/src/hooks/dhcp/high_availability/ha_config_parser.cc b/src/hooks/dhcp/high_availability/ha_config_parser.cc index 4f44a7d0c1..8bc0aaa5ba 100644 --- a/src/hooks/dhcp/high_availability/ha_config_parser.cc +++ b/src/hooks/dhcp/high_availability/ha_config_parser.cc @@ -32,6 +32,7 @@ const SimpleDefaults HA_CONFIG_DEFAULTS = { { "max-response-delay", Element::integer, "60000" }, { "max-unacked-clients", Element::integer, "10" }, { "require-client-certs", Element::boolean, "true" }, + { "restrict-commands", Element::boolean, "false" }, { "send-lease-updates", Element::boolean, "true" }, { "sync-leases", Element::boolean, "true" }, { "sync-timeout", Element::integer, "60000" }, @@ -233,6 +234,9 @@ HAConfigParser::parseInternal(const HAConfigPtr& config_storage, // Get 'require-client-certs'. config_storage->setRequireClientCerts(getBoolean(c, "require-client-certs")); + // Get 'restrict-commands'. + config_storage->setRestrictCommands(getBoolean(c, "restrict-commands")); + // Peers configuration parsing. const auto& peers_vec = peers->listValue(); diff --git a/src/hooks/dhcp/high_availability/ha_service.cc b/src/hooks/dhcp/high_availability/ha_service.cc index 416bf052f3..f844b33b04 100644 --- a/src/hooks/dhcp/high_availability/ha_service.cc +++ b/src/hooks/dhcp/high_availability/ha_service.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -114,6 +115,16 @@ HAService::HAService(const IOServicePtr& io_service, const NetworkStatePtr& netw // Instantiate the listener. listener_.reset(new CmdHttpListener(server_address, my_url.getPort(), listener_threads, tls_context)); + // Set the command filter when enabled. + if (config_->getRestrictCommands()) { + if (server_type == HAServerType::DHCPv4) { + CmdResponseCreator::command_accept_list_ = + CommandCreator::ha_commands4_; + } else { + CmdResponseCreator::command_accept_list_ = + CommandCreator::ha_commands6_; + } + } } } diff --git a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc index 35b4cfbb9c..5c773a149f 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc @@ -1333,6 +1333,7 @@ TEST_F(HAConfigTest, tlsParameterInheritance) { " \"cert-file\": \"!CA!/kea-client.crt\"," " \"key-file\": \"!CA!/kea-client.key\"," " \"require-client-certs\": false," + " \"restrict-commands\": true," " \"peers\": [" " {" " \"name\": \"my-server\"," @@ -1381,6 +1382,7 @@ TEST_F(HAConfigTest, tlsParameterInheritance) { expected += "/kea-client.key"; EXPECT_EQ(expected, impl->getConfig()->getKeyFile().get()); EXPECT_FALSE(impl->getConfig()->getRequireClientCerts()); + EXPECT_TRUE(impl->getConfig()->getRestrictCommands()); // Check the first peer parameters: it inherits them from the global level. HAConfig::PeerConfigPtr cfg = impl->getConfig()->getThisServerConfig(); diff --git a/src/hooks/dhcp/high_availability/tests/ha_mt_unittest.cc b/src/hooks/dhcp/high_availability/tests/ha_mt_unittest.cc index 3c3f32d541..eded087b63 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_mt_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/ha_mt_unittest.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -125,6 +126,7 @@ public: HAMtServiceTest() : HATest() { MultiThreadingMgr::instance().setMode(true); + CmdResponseCreator::command_accept_list_.clear(); } /// @brief Destructor. @@ -134,6 +136,7 @@ public: io_service_->get_io_service().reset(); io_service_->poll(); MultiThreadingMgr::instance().setMode(false); + CmdResponseCreator::command_accept_list_.clear(); } /// @brief Callback function invoke upon test timeout. @@ -162,6 +165,7 @@ TEST_F(HAMtServiceTest, multiThreadingBasics) { " \"this-server-name\": \"server1\"," " \"mode\": \"passive-backup\"," " \"wait-backup-ack\": true," + " \"restrict-commands\": true," " \"peers\": [" " {" " \"name\": \"server1\"," @@ -195,6 +199,9 @@ TEST_F(HAMtServiceTest, multiThreadingBasics) { // Multi-threading should be enabled. ASSERT_TRUE(ha_config->getEnableMultiThreading()); + // Command filtering is enabled. + EXPECT_FALSE(CmdResponseCreator::command_accept_list_.empty()); + // Now we'll start, pause, resume and stop a few times. for (int i = 0; i < 3; ++i) { // Verify we're stopped.