From: Thomas Markwalder Date: Mon, 19 May 2025 19:49:23 +0000 (-0400) Subject: [#3840] Backport ctl channel socket restrictions X-Git-Tag: Kea-2.6.3~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=43bba7799f6892f739b4745b35bbeacef3645ad3;p=thirdparty%2Fkea.git [#3840] Backport ctl channel socket restrictions On branch 3840-restrict-ctl-sockets-v6 Changes to be committed: modified: doc/examples/agent/comments.json modified: doc/examples/agent/simple.json modified: doc/examples/ddns/all-keys-netconf.json modified: doc/examples/ddns/all-keys.json modified: doc/examples/ddns/comments.json modified: doc/examples/ddns/sample1.json modified: doc/examples/ddns/template.json modified: doc/examples/kea4/advanced.json modified: doc/examples/kea4/all-keys-netconf.json modified: doc/examples/kea4/all-keys.json modified: doc/examples/kea4/comments.json modified: doc/examples/kea4/config-backend.json modified: doc/examples/kea4/dhcpv4-over-dhcpv6.json modified: doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json modified: doc/examples/kea4/ha-load-balancing-server2-mt.json modified: doc/examples/kea6/advanced.json modified: doc/examples/kea6/all-keys-netconf.json modified: doc/examples/kea6/all-keys.json modified: doc/examples/kea6/comments.json modified: doc/examples/kea6/config-backend.json modified: doc/examples/kea6/dhcpv4-over-dhcpv6.json modified: doc/examples/kea6/ha-hot-standby-server1-with-tls.json modified: doc/examples/kea6/ha-hot-standby-server2.json modified: doc/examples/netconf/comments.json modified: doc/examples/netconf/simple-dhcp4.json modified: doc/examples/netconf/simple-dhcp6.json modified: doc/sphinx/arm/agent.rst modified: doc/sphinx/arm/ddns.rst modified: doc/sphinx/arm/dhcp4-srv.rst modified: doc/sphinx/arm/dhcp6-srv.rst modified: doc/sphinx/arm/hooks.rst modified: src/bin/agent/ca_cfg_mgr.cc modified: src/bin/agent/ca_command_mgr.cc modified: src/bin/agent/simple_parser.cc modified: src/bin/agent/tests/ca_cfg_mgr_unittests.cc modified: src/bin/agent/tests/ca_command_mgr_unittests.cc modified: src/bin/agent/tests/ca_controller_unittests.cc modified: src/bin/agent/tests/get_config_unittest.cc modified: src/bin/agent/tests/parser_unittests.cc modified: src/bin/agent/tests/testdata/get_config.json modified: src/bin/d2/tests/d2_cfg_mgr_unittests.cc modified: src/bin/d2/tests/d2_command_unittest.cc modified: src/bin/d2/tests/get_config_unittest.cc modified: src/bin/d2/tests/testdata/get_config.json modified: src/bin/dhcp4/tests/config_parser_unittest.cc modified: src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc modified: src/bin/dhcp4/tests/dhcp4_srv_unittest.cc modified: src/bin/dhcp4/tests/dhcp4_test_utils.cc modified: src/bin/dhcp4/tests/dhcp4_test_utils.h modified: src/bin/dhcp4/tests/get_config_unittest.cc modified: src/bin/dhcp6/tests/config_parser_unittest.cc modified: src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc modified: src/bin/dhcp6/tests/dhcp6_srv_unittest.cc modified: src/bin/dhcp6/tests/dhcp6_test_utils.cc modified: src/bin/dhcp6/tests/dhcp6_test_utils.h modified: src/bin/dhcp6/tests/get_config_unittest.cc modified: src/bin/keactrl/kea-ctrl-agent.conf.pre modified: src/bin/keactrl/kea-dhcp-ddns.conf.pre modified: src/bin/keactrl/kea-dhcp4.conf.pre modified: src/bin/keactrl/kea-dhcp6.conf.pre modified: src/bin/keactrl/kea-netconf.conf.pre modified: src/bin/netconf/tests/testdata/get_config.json modified: src/lib/config/Makefile.am modified: src/lib/config/command_mgr.cc modified: src/lib/config/tests/Makefile.am modified: src/lib/config/tests/command_mgr_unittests.cc new file: src/lib/config/tests/unix_command_config_unittests.cc new file: src/lib/config/unix_command_config.cc new file: src/lib/config/unix_command_config.h modified: src/lib/util/filesystem.cc modified: src/lib/util/filesystem.h modified: src/lib/util/tests/filesystem_unittests.cc --- diff --git a/doc/examples/agent/comments.json b/doc/examples/agent/comments.json index 10c82e3fa3..6803165ae0 100644 --- a/doc/examples/agent/comments.json +++ b/doc/examples/agent/comments.json @@ -43,7 +43,7 @@ { "comment": "control socket for DHCPv4 server", "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" } }, diff --git a/doc/examples/agent/simple.json b/doc/examples/agent/simple.json index 5e82b99530..1b249b2479 100644 --- a/doc/examples/agent/simple.json +++ b/doc/examples/agent/simple.json @@ -86,21 +86,21 @@ { "comment": "socket to DHCPv4 server", "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Location of the DHCPv6 command channel socket. "dhcp6": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Location of the D2 command channel socket. "d2": { "socket-type": "unix", - "socket-name": "/tmp/kea-ddns-ctrl-socket", + "socket-name": "kea-ddns-ctrl-socket", "user-context": { "in-use": false } } }, diff --git a/doc/examples/ddns/all-keys-netconf.json b/doc/examples/ddns/all-keys-netconf.json index 642258dc35..be481f6851 100644 --- a/doc/examples/ddns/all-keys-netconf.json +++ b/doc/examples/ddns/all-keys-netconf.json @@ -40,7 +40,7 @@ // Location of the UNIX domain socket file the DHCP-DDNS server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea-ddns-ctrl-socket", + "socket-name": "kea-ddns-ctrl-socket", // Control socket type used by the Kea DHCP-DDNS server. // The 'unix' socket is currently the only supported type. diff --git a/doc/examples/ddns/all-keys.json b/doc/examples/ddns/all-keys.json index bcb4735d98..5d562c85f4 100644 --- a/doc/examples/ddns/all-keys.json +++ b/doc/examples/ddns/all-keys.json @@ -40,7 +40,7 @@ // Location of the UNIX domain socket file the DHCP-DDNS server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea-ddns-ctrl-socket", + "socket-name": "kea-ddns-ctrl-socket", // Control socket type used by the Kea DHCP-DDNS server. // The 'unix' socket is currently the only supported type. diff --git a/doc/examples/ddns/comments.json b/doc/examples/ddns/comments.json index a7717d17c8..750c27d099 100644 --- a/doc/examples/ddns/comments.json +++ b/doc/examples/ddns/comments.json @@ -16,7 +16,7 @@ { "comment": "Control channel", "socket-type": "unix", - "socket-name": "/tmp/kea-ddns-ctrl-socket" + "socket-name": "kea-ddns-ctrl-socket" }, "forward-ddns": diff --git a/doc/examples/ddns/sample1.json b/doc/examples/ddns/sample1.json index 4d8148bc29..2c43ba920a 100644 --- a/doc/examples/ddns/sample1.json +++ b/doc/examples/ddns/sample1.json @@ -32,7 +32,7 @@ "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea-ddns-ctrl-socket" + "socket-name": "kea-ddns-ctrl-socket" }, // ----------------- Hooks Libraries ----------------- diff --git a/doc/examples/ddns/template.json b/doc/examples/ddns/template.json index c4da2508e5..74dffdede5 100644 --- a/doc/examples/ddns/template.json +++ b/doc/examples/ddns/template.json @@ -19,7 +19,7 @@ // "control-socket": // { // "socket-type": "unix", -// "socket-name": "/tmp/kea-ddns-ctrl-socket" +// "socket-name": "kea-ddns-ctrl-socket" // }, // ----------------- Forward DDNS ------------------ diff --git a/doc/examples/kea4/advanced.json b/doc/examples/kea4/advanced.json index 30d3333ac6..f0b810ea46 100644 --- a/doc/examples/kea4/advanced.json +++ b/doc/examples/kea4/advanced.json @@ -89,7 +89,7 @@ // Guide for list of supported commands. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Addresses will be assigned with a lifetime of 4000 seconds. diff --git a/doc/examples/kea4/all-keys-netconf.json b/doc/examples/kea4/all-keys-netconf.json index 9182fdf1e6..42d5062404 100644 --- a/doc/examples/kea4/all-keys-netconf.json +++ b/doc/examples/kea4/all-keys-netconf.json @@ -147,7 +147,7 @@ // Location of the UNIX domain socket file the DHCPv4 server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea4-ctrl-socket", + "socket-name": "kea4-ctrl-socket", // Control socket type used by the Kea DHCPv4 server. The 'unix' // socket is currently the only supported type. diff --git a/doc/examples/kea4/all-keys.json b/doc/examples/kea4/all-keys.json index 098119869d..ea00a07304 100644 --- a/doc/examples/kea4/all-keys.json +++ b/doc/examples/kea4/all-keys.json @@ -147,7 +147,7 @@ // Location of the UNIX domain socket file the DHCPv4 server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea4-ctrl-socket", + "socket-name": "kea4-ctrl-socket", // Control socket type used by the Kea DHCPv4 server. The 'unix' // socket is currently the only supported type. diff --git a/doc/examples/kea4/comments.json b/doc/examples/kea4/comments.json index a5cfbdcd5c..c8f4175fd3 100644 --- a/doc/examples/kea4/comments.json +++ b/doc/examples/kea4/comments.json @@ -54,7 +54,7 @@ // In control socket (more for the agent) "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket", + "socket-name": "kea4-ctrl-socket", "user-context": { "comment": "Indirect comment" } }, diff --git a/doc/examples/kea4/config-backend.json b/doc/examples/kea4/config-backend.json index 82d36fb041..6489971213 100644 --- a/doc/examples/kea4/config-backend.json +++ b/doc/examples/kea4/config-backend.json @@ -52,7 +52,7 @@ // details. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Hooks libraries that enable configuration backend are loaded. diff --git a/doc/examples/kea4/dhcpv4-over-dhcpv6.json b/doc/examples/kea4/dhcpv4-over-dhcpv6.json index 7909c72e6e..ff338470d8 100644 --- a/doc/examples/kea4/dhcpv4-over-dhcpv6.json +++ b/doc/examples/kea4/dhcpv4-over-dhcpv6.json @@ -36,7 +36,7 @@ "name": "kea-dhcp4", "output-options": [ { - "output": "/tmp/kea-dhcp4.log" + "output": "kea-dhcp4.log" } ], "severity": "DEBUG", diff --git a/doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json b/doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json index fdef507e70..8ece46642e 100644 --- a/doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json +++ b/doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json @@ -25,7 +25,7 @@ // The Control Agent is used only to handle user commands. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Multi-threading parameters. diff --git a/doc/examples/kea4/ha-load-balancing-server2-mt.json b/doc/examples/kea4/ha-load-balancing-server2-mt.json index 154a129f8a..94f81cb6ed 100644 --- a/doc/examples/kea4/ha-load-balancing-server2-mt.json +++ b/doc/examples/kea4/ha-load-balancing-server2-mt.json @@ -24,7 +24,7 @@ // The Control Agent is used only to handle user commands. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Multi-threading parameters. diff --git a/doc/examples/kea6/advanced.json b/doc/examples/kea6/advanced.json index 6d4537c64a..53edc1285c 100644 --- a/doc/examples/kea6/advanced.json +++ b/doc/examples/kea6/advanced.json @@ -80,7 +80,7 @@ // Guide for list of supported commands. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Addresses will be assigned with preferred and valid lifetimes diff --git a/doc/examples/kea6/all-keys-netconf.json b/doc/examples/kea6/all-keys-netconf.json index 86a9f8c122..b0c77438e7 100644 --- a/doc/examples/kea6/all-keys-netconf.json +++ b/doc/examples/kea6/all-keys-netconf.json @@ -98,7 +98,7 @@ // Location of the UNIX domain socket file the DHCPv6 server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea6-ctrl-socket", + "socket-name": "kea6-ctrl-socket", // Control socket type used by the Kea DHCPv6 server. The 'unix' // socket is currently the only supported type. diff --git a/doc/examples/kea6/all-keys.json b/doc/examples/kea6/all-keys.json index 4d4fbebeed..326324aaa2 100644 --- a/doc/examples/kea6/all-keys.json +++ b/doc/examples/kea6/all-keys.json @@ -98,7 +98,7 @@ // Location of the UNIX domain socket file the DHCPv6 server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea6-ctrl-socket", + "socket-name": "kea6-ctrl-socket", // Control socket type used by the Kea DHCPv6 server. The 'unix' // socket is currently the only supported type. diff --git a/doc/examples/kea6/comments.json b/doc/examples/kea6/comments.json index 88cdd41376..65ad2f0e25 100644 --- a/doc/examples/kea6/comments.json +++ b/doc/examples/kea6/comments.json @@ -54,7 +54,7 @@ // In control socket (more for the agent) "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket", + "socket-name": "kea6-ctrl-socket", "user-context": { "comment": "Indirect comment" } }, diff --git a/doc/examples/kea6/config-backend.json b/doc/examples/kea6/config-backend.json index d269f3f120..9db07342e1 100644 --- a/doc/examples/kea6/config-backend.json +++ b/doc/examples/kea6/config-backend.json @@ -52,7 +52,7 @@ // details. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Hooks libraries that enable configuration backend are loaded. diff --git a/doc/examples/kea6/dhcpv4-over-dhcpv6.json b/doc/examples/kea6/dhcpv4-over-dhcpv6.json index e723f69d01..698fc96481 100644 --- a/doc/examples/kea6/dhcpv4-over-dhcpv6.json +++ b/doc/examples/kea6/dhcpv4-over-dhcpv6.json @@ -46,7 +46,7 @@ "name": "kea-dhcp6", "output-options": [ { - "output": "/tmp/kea-dhcp6.log" + "output": "kea-dhcp6.log" } ], "severity": "DEBUG", diff --git a/doc/examples/kea6/ha-hot-standby-server1-with-tls.json b/doc/examples/kea6/ha-hot-standby-server1-with-tls.json index 9fab5429c9..6e67645b57 100644 --- a/doc/examples/kea6/ha-hot-standby-server1-with-tls.json +++ b/doc/examples/kea6/ha-hot-standby-server1-with-tls.json @@ -24,7 +24,7 @@ // API between the HA peers. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Use Memfile lease database backend to store leases in a CSV file. diff --git a/doc/examples/kea6/ha-hot-standby-server2.json b/doc/examples/kea6/ha-hot-standby-server2.json index 6a9a680ed9..037fd97810 100644 --- a/doc/examples/kea6/ha-hot-standby-server2.json +++ b/doc/examples/kea6/ha-hot-standby-server2.json @@ -23,7 +23,7 @@ // API between the HA peers. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Use Memfile lease database backend to store leases in a CSV file. diff --git a/doc/examples/netconf/comments.json b/doc/examples/netconf/comments.json index 99014e7850..12e1276efc 100644 --- a/doc/examples/netconf/comments.json +++ b/doc/examples/netconf/comments.json @@ -20,7 +20,7 @@ { "comment": "using unix/local socket", "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" } } }, diff --git a/doc/examples/netconf/simple-dhcp4.json b/doc/examples/netconf/simple-dhcp4.json index 887c673065..6a761d9a83 100644 --- a/doc/examples/netconf/simple-dhcp4.json +++ b/doc/examples/netconf/simple-dhcp4.json @@ -57,7 +57,7 @@ "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Comment is optional. You can put some notes here. diff --git a/doc/examples/netconf/simple-dhcp6.json b/doc/examples/netconf/simple-dhcp6.json index 1829a44a2b..03b6ab8118 100644 --- a/doc/examples/netconf/simple-dhcp6.json +++ b/doc/examples/netconf/simple-dhcp6.json @@ -58,7 +58,7 @@ "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Comment is optional. You can put some notes here. diff --git a/doc/sphinx/arm/agent.rst b/doc/sphinx/arm/agent.rst index 328c03a894..1581a05976 100644 --- a/doc/sphinx/arm/agent.rst +++ b/doc/sphinx/arm/agent.rst @@ -135,6 +135,16 @@ for the DHCPv4 server and the CA (for that server) must match. Consult :ref:`d2-ctrl-channel` to learn how the socket configuration is specified for the DHCPv4, DHCPv6, and D2 services. +.. note:: + + As of Kea 2.6.3, control sockets may only reside in the directory + determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This + path may be overridden at startup by setting the environment variable + ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than + this value is used in ``socket-name``, Kea will emit an error and refuse to + start or, if already running, log an unrecoverable error. For ease of use in + simply omit the path component from ``socket-name``. + User contexts can store arbitrary data as long as they are in valid JSON syntax and their top-level element is a map (i.e. the data must be enclosed in curly brackets). Some hook libraries may expect specific diff --git a/doc/sphinx/arm/ddns.rst b/doc/sphinx/arm/ddns.rst index 4fac991001..7cb114dac5 100644 --- a/doc/sphinx/arm/ddns.rst +++ b/doc/sphinx/arm/ddns.rst @@ -303,6 +303,16 @@ operating system, i.e. the size of the ``sun_path`` field in the different operating systems, between 91 and 107 characters. Typical values are 107 on Linux and 103 on FreeBSD. +.. note:: + + As of Kea 2.6.3, control sockets may only reside in the directory + determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This + path may be overridden at startup by setting the environment variable + ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than + this value is used in ``socket-name``, Kea will emit an error and refuse to + start or, if already running, log an unrecoverable error. For ease of use in + simply omit the path component from ``socket-name``. + Communication over the control channel is conducted using JSON structures. See the `Control Channel section in the Kea Developer's Guide `__ diff --git a/doc/sphinx/arm/dhcp4-srv.rst b/doc/sphinx/arm/dhcp4-srv.rst index 25bea8dab6..8b9e21f36f 100644 --- a/doc/sphinx/arm/dhcp4-srv.rst +++ b/doc/sphinx/arm/dhcp4-srv.rst @@ -7588,6 +7588,16 @@ operating system, i.e. the size of the ``sun_path`` field in the different operating systems, between 91 and 107 characters. Typical values are 107 on Linux and 103 on FreeBSD. +.. note:: + + As of Kea 2.6.3, control sockets may only reside in the directory + determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This + path may be overridden at startup by setting the environment variable + ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than + this value is used in ``socket-name``, Kea will emit an error and refuse to + start or, if already running, log an unrecoverable error. For ease of use in + simply omit the path component from ``socket-name``. + Communication over the control channel is conducted using JSON structures. See the `Control Channel section in the Kea Developer's Guide diff --git a/doc/sphinx/arm/dhcp6-srv.rst b/doc/sphinx/arm/dhcp6-srv.rst index 09931616ad..9f30d28206 100644 --- a/doc/sphinx/arm/dhcp6-srv.rst +++ b/doc/sphinx/arm/dhcp6-srv.rst @@ -7419,6 +7419,16 @@ operating system, i.e. the size of the ``sun_path`` field in the different operating systems, between 91 and 107 characters. Typical values are 107 on Linux and 103 on FreeBSD. +.. note:: + + As of Kea 2.6.3, control sockets may only reside in the directory + determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This + path may be overridden at startup by setting the environment variable + ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than + this value is used in ``socket-name``, Kea will emit an error and refuse to + start or, if already running, log an unrecoverable error. For ease of use in + simply omit the path component from ``socket-name``. + Communication over the control channel is conducted using JSON structures. See the `Control Channel section in the Kea Developer's Guide diff --git a/doc/sphinx/arm/hooks.rst b/doc/sphinx/arm/hooks.rst index 8d037a5735..f420848ed2 100644 --- a/doc/sphinx/arm/hooks.rst +++ b/doc/sphinx/arm/hooks.rst @@ -215,7 +215,7 @@ configuration would be: As of Kea 2.6.3, hook libraries may only be loaded from the default installation directory determined during compilation and shown in the config report as -"Hooks directory". This value may be overridden at startup by setting the +"Hooks directory". This value may be overridden at startup by setting the environment variable ``KEA_HOOKS_PATH`` to the desired path. If a path other than this value is used in a ``library`` element Kea will emit an error and refuse to load the library. For ease of use ``library`` elements may simply omit path diff --git a/src/bin/agent/ca_cfg_mgr.cc b/src/bin/agent/ca_cfg_mgr.cc index f1d1047917..a5ad667bbf 100644 --- a/src/bin/agent/ca_cfg_mgr.cc +++ b/src/bin/agent/ca_cfg_mgr.cc @@ -201,9 +201,13 @@ CtrlAgentCfgContext::toElement() const { // Set control-sockets ElementPtr control_sockets = Element::createMap(); for (auto const& si : ctrl_sockets_) { - ConstElementPtr socket = UserContext::toElement(si.second); - control_sockets->set(si.first, socket); + // Remove validated_path. + auto mutable_socket_info = boost::const_pointer_cast( + UserContext::toElement(si.second)); + mutable_socket_info->remove("validated-socket-name"); + control_sockets->set(si.first, mutable_socket_info); } + ca->set("control-sockets", control_sockets); // Set Control-agent ElementPtr result = Element::createMap(); diff --git a/src/bin/agent/ca_command_mgr.cc b/src/bin/agent/ca_command_mgr.cc index 9411181dc4..7c51c72246 100644 --- a/src/bin/agent/ca_command_mgr.cc +++ b/src/bin/agent/ca_command_mgr.cc @@ -220,9 +220,15 @@ CtrlAgentCommandMgr::forwardCommand(const std::string& service, " for the server type " << service); } - // If the configuration does its job properly the socket-name must be - // specified and must be a string value. - std::string socket_name = socket_info->get("socket-name")->stringValue(); + // If the configuration does its job properly the validated-socket-name + // should be present. + if (!socket_info->get("validated-socket-name")) { + isc_throw(Unexpected, "validated-socket-name missing from " + << " socket_info: " << socket_info->str() + << " for the server type " << service); + } + + auto socket_name = socket_info->get("validated-socket-name")->stringValue(); // Forward command and receive reply. IOServicePtr io_service(new IOService());; diff --git a/src/bin/agent/simple_parser.cc b/src/bin/agent/simple_parser.cc index 7a6a76372c..87fea25cee 100644 --- a/src/bin/agent/simple_parser.cc +++ b/src/bin/agent/simple_parser.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2024 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2017-2025 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 @@ -10,12 +10,14 @@ #include #include #include +#include #include #include #include using namespace isc::data; using namespace isc::asiolink; +using namespace isc::config; namespace isc { namespace agent { @@ -149,7 +151,14 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx, if (ctrl_sockets) { auto const& sockets_map = ctrl_sockets->mapValue(); for (auto const& cs : sockets_map) { - ctx->setControlSocketInfo(cs.second, cs.first); + // Add a validated socket name so we can suppress it in + // toElement() but don't have to revalidate it every time we + // want to use it. + auto mutable_socket_info = boost::const_pointer_cast(cs.second); + std::string socket_name = mutable_socket_info->get("socket-name")->stringValue(); + auto validated_name = UnixCommandConfig::validatePath(socket_name); + mutable_socket_info->set("validated-socket-name", Element::create(validated_name)); + ctx->setControlSocketInfo(mutable_socket_info, cs.first); } } diff --git a/src/bin/agent/tests/ca_cfg_mgr_unittests.cc b/src/bin/agent/tests/ca_cfg_mgr_unittests.cc index 20263d74b4..b932ba26b2 100644 --- a/src/bin/agent/tests/ca_cfg_mgr_unittests.cc +++ b/src/bin/agent/tests/ca_cfg_mgr_unittests.cc @@ -7,11 +7,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -23,6 +25,8 @@ using namespace isc::data; using namespace isc::hooks; using namespace isc::http; using namespace isc::process; +using namespace isc::config; +using namespace isc::util; namespace { @@ -239,8 +243,8 @@ TEST(CtrlAgentCfgMgr, contextAuthConfigFile) { auth->setRealm("foobar"); auth->setDirectory("/tmp"); - auth->add("", "/tmp/foo", "", "/tmp/bar"); - auth->add("", "/tmp/test", "", "/tmp/pwd"); + auth->add("", "foo", "", "bar"); + auth->add("", "test", "", "pwd"); const HttpAuthConfigPtr& stored_auth = ctx.getAuthConfig(); ASSERT_TRUE(stored_auth); @@ -265,7 +269,7 @@ const char* AGENT_CONFIGS[] = { " \"http-port\": 8001,\n" " \"control-sockets\": {\n" " \"dhcp4\": {\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " }\n" " }\n" "}", @@ -276,13 +280,13 @@ const char* AGENT_CONFIGS[] = { " \"http-port\": 8001,\n" " \"control-sockets\": {\n" " \"dhcp4\": {\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " },\n" " \"dhcp6\": {\n" - " \"socket-name\": \"/tmp/socket-v6\"\n" + " \"socket-name\": \"socket-v6\"\n" " },\n" " \"d2\": {\n" - " \"socket-name\": \"/tmp/socket-d2\"\n" + " \"socket-name\": \"socket-d2\"\n" " }\n" " }\n" "}", @@ -295,7 +299,7 @@ const char* AGENT_CONFIGS[] = { " \"http-port\": 8001,\n" " \"control-sockets\": {\n" " \"dhcp4\": {\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " }\n" " },\n" " \"hooks-libraries\": [" @@ -314,7 +318,7 @@ const char* AGENT_CONFIGS[] = { " \"http-port\": 8001,\n" " \"control-sockets\": {\n" " \"d2\": {\n" - " \"socket-name\": \"/tmp/socket-d2\"\n" + " \"socket-name\": \"socket-d2\"\n" " }\n" " }\n" "}", @@ -325,7 +329,7 @@ const char* AGENT_CONFIGS[] = { " \"http-port\": 8001,\n" " \"control-sockets\": {\n" " \"dhcp6\": {\n" - " \"socket-name\": \"/tmp/socket-v6\"\n" + " \"socket-name\": \"socket-v6\"\n" " }\n" " }\n" "}", @@ -349,7 +353,7 @@ const char* AGENT_CONFIGS[] = { " },\n" " \"control-sockets\": {\n" " \"dhcp4\": {\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " }\n" " }\n" "}", @@ -377,10 +381,10 @@ const char* AGENT_CONFIGS[] = { " \"control-sockets\": {\n" " \"dhcp4\": {\n" " \"comment\": \"dhcp4 socket\",\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " },\n" " \"dhcp6\": {\n" - " \"socket-name\": \"/tmp/socket-v6\",\n" + " \"socket-name\": \"socket-v6\",\n" " \"user-context\": { \"version\": 1 }\n" " }\n" " }\n" @@ -415,7 +419,7 @@ const char* AGENT_CONFIGS[] = { " },\n" " \"control-sockets\": {\n" " \"dhcp4\": {\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " }\n" " }\n" "}" @@ -427,11 +431,13 @@ public: /// @brief Constructor. AgentParserTest() { resetHooksPath(); + setSocketTestPath(); } /// @brief Destructor. virtual ~AgentParserTest() { resetHooksPath(); + resetSocketPath(); } /// @brief Sets the Hooks path from which hooks can be loaded. @@ -447,6 +453,20 @@ public: HooksLibrariesParser::getHooksPath(true); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Tries to load input text as a configuration /// /// @param config text containing input configuration @@ -470,6 +490,20 @@ public: return (txt); } + /// @brief Make expected contents of socket info with socket path added. + /// + /// @param name name of the socket + /// @return expected string + std::string makeSocketStr(const std::string& name) { + std::ostringstream os; + os << "{ \"socket-name\": \"" + << name << "\", \"socket-type\": \"unix\"," + << " \"validated-socket-name\": \"" + << UnixCommandConfig::getSocketPath() << "/" << name + << "\" }"; + return (os.str()); + } + /// Configuration Manager (used in tests) NakedAgentCfgMgr cfg_mgr_; }; @@ -503,8 +537,7 @@ TEST_F(AgentParserTest, configParseSocketDhcp4) { ASSERT_TRUE(ctx); ConstElementPtr socket = ctx->getControlSocketInfo("dhcp4"); ASSERT_TRUE(socket); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }", - socket->str()); + EXPECT_EQ(makeSocketStr("socket-v4"), socket->str()); EXPECT_FALSE(ctx->getControlSocketInfo("dhcp6")); EXPECT_FALSE(ctx->getControlSocketInfo("d2")); } @@ -519,9 +552,7 @@ TEST_F(AgentParserTest, configParseSocketD2) { ASSERT_TRUE(ctx); ConstElementPtr socket = ctx->getControlSocketInfo("d2"); ASSERT_TRUE(socket); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-d2\", \"socket-type\": \"unix\" }", - socket->str()); - + EXPECT_EQ(makeSocketStr("socket-d2"), socket->str()); EXPECT_FALSE(ctx->getControlSocketInfo("dhcp4")); EXPECT_FALSE(ctx->getControlSocketInfo("dhcp6")); } @@ -536,8 +567,7 @@ TEST_F(AgentParserTest, configParseSocketDhcp6) { ASSERT_TRUE(ctx); ConstElementPtr socket = ctx->getControlSocketInfo("dhcp6"); ASSERT_TRUE(socket); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v6\", \"socket-type\": \"unix\" }", - socket->str()); + EXPECT_EQ(makeSocketStr("socket-v6"), socket->str()); EXPECT_FALSE(ctx->getControlSocketInfo("dhcp4")); EXPECT_FALSE(ctx->getControlSocketInfo("d2")); } @@ -552,14 +582,11 @@ TEST_F(AgentParserTest, configParse3Sockets) { ConstElementPtr socket4 = ctx->getControlSocketInfo("dhcp4"); ConstElementPtr socket6 = ctx->getControlSocketInfo("dhcp6"); ASSERT_TRUE(socket2); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-d2\", \"socket-type\": \"unix\" }", - socket2->str()); + EXPECT_EQ(makeSocketStr("socket-d2"), socket2->str()); ASSERT_TRUE(socket4); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }", - socket4->str()); + EXPECT_EQ(makeSocketStr("socket-v4"), socket4->str()); ASSERT_TRUE(socket6); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v6\", \"socket-type\": \"unix\" }", - socket6->str()); + EXPECT_EQ(makeSocketStr("socket-v6"), socket6->str()); } // This test checks that the config file with hook library specified can be diff --git a/src/bin/agent/tests/ca_command_mgr_unittests.cc b/src/bin/agent/tests/ca_command_mgr_unittests.cc index 4cef0426cd..31f36bedef 100644 --- a/src/bin/agent/tests/ca_command_mgr_unittests.cc +++ b/src/bin/agent/tests/ca_command_mgr_unittests.cc @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -28,6 +30,8 @@ using namespace isc::agent; using namespace isc::asiolink; using namespace isc::data; using namespace isc::process; +using namespace isc::config; +using namespace isc::util; namespace { @@ -50,6 +54,7 @@ public: : DControllerTest(CtrlAgentController::instance), mgr_(CtrlAgentCommandMgr::instance()) { mgr_.deregisterAll(); + setSocketTestPath(); removeUnixSocketFile(); initProcess(); } @@ -60,6 +65,7 @@ public: virtual ~CtrlAgentCommandMgrTest() { mgr_.deregisterAll(); removeUnixSocketFile(); + resetSocketPath(); } /// @brief Verifies received answer @@ -106,19 +112,22 @@ public: } /// @brief Returns socket file path. - /// - /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the - /// socket file is created in the location pointed to by this variable. - /// Otherwise, it is created in the build directory. std::string unixSocketFilePath() { - std::string socket_path; - const char* env = getenv("KEA_SOCKET_TEST_DIR"); - if (env) { - socket_path = std::string(env) + "/test-socket"; - } else { - socket_path = sandbox.join("test-socket"); - } - return (socket_path); + return (UnixCommandConfig::getSocketPath() + "/test-socket"); + } + + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); } /// @brief Removes unix socket descriptor. @@ -158,7 +167,7 @@ public: ASSERT_TRUE(ctx); ElementPtr control_socket = Element::createMap(); - control_socket->set("socket-name", + control_socket->set("validated-socket-name", Element::create(unixSocketFilePath())); ctx->setControlSocketInfo(control_socket, service); } diff --git a/src/bin/agent/tests/ca_controller_unittests.cc b/src/bin/agent/tests/ca_controller_unittests.cc index 44f2b2e4a2..e34649c8f4 100644 --- a/src/bin/agent/tests/ca_controller_unittests.cc +++ b/src/bin/agent/tests/ca_controller_unittests.cc @@ -12,7 +12,9 @@ #include #include #include +#include #include +#include #include #include @@ -26,6 +28,8 @@ using namespace isc::agent; using namespace isc::data; using namespace isc::http; using namespace isc::process; +using namespace isc::config; +using namespace isc::util; using namespace boost::posix_time; using namespace std; @@ -39,11 +43,11 @@ const char* valid_agent_config = " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/first/dhcp4/socket\"" + " \"socket-name\": \"first_socket4\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/first/dhcp6/socket\"" + " \"socket-name\": \"first_socket6\"" " }" " }" "}"; @@ -60,6 +64,12 @@ public: /// @brief Constructor. CtrlAgentControllerTest() : DControllerTest(CtrlAgentController::instance) { + setSocketTestPath(); + } + + /// @brief Destructor. + virtual ~CtrlAgentControllerTest() { + resetSocketPath(); } /// @brief Returns pointer to CtrlAgentProcess instance. @@ -85,6 +95,20 @@ public: return (p); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Tests that socket info structure contains 'unix' socket-type /// value and the expected socket-name. /// @@ -100,8 +124,7 @@ public: ASSERT_TRUE(sock_info->contains("socket-type")); EXPECT_EQ("unix", sock_info->get("socket-type")->stringValue()); ASSERT_TRUE(sock_info->contains("socket-name")); - EXPECT_EQ(exp_socket_name, - sock_info->get("socket-name")->stringValue()); + EXPECT_EQ(exp_socket_name, sock_info->get("socket-name")->stringValue()); } /// @brief Compares the status in the given parse result to a given value. @@ -279,11 +302,11 @@ TEST_F(CtrlAgentControllerTest, successfulConfigUpdate) { " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp4/socket\"" + " \"socket-name\": \"second_socket6\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp6/socket\"" + " \"socket-name\": \"second_socket6\"" " }" " }" "}"; @@ -322,8 +345,8 @@ TEST_F(CtrlAgentControllerTest, successfulConfigUpdate) { EXPECT_EQ(8080, ctx->getHttpPort()); // The forwarding configuration should have been updated too. - testUnixSocketInfo("dhcp4", "/second/dhcp4/socket"); - testUnixSocketInfo("dhcp6", "/second/dhcp6/socket"); + testUnixSocketInfo("dhcp4", "second_socket6"); + testUnixSocketInfo("dhcp6", "second_socket6"); // After the shutdown the HTTP listener no longer exists. CtrlAgentProcessPtr process = getCtrlAgentProcess(); @@ -345,11 +368,11 @@ TEST_F(CtrlAgentControllerTest, unsuccessfulConfigUpdate) { " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp4/socket\"" + " \"socket-name\": \"second_socket6\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp6/socket\"" + " \"socket-name\": \"second_socket6\"" " }" " }" "}"; @@ -388,8 +411,8 @@ TEST_F(CtrlAgentControllerTest, unsuccessfulConfigUpdate) { EXPECT_EQ(8081, ctx->getHttpPort()); // Same for forwarding. - testUnixSocketInfo("dhcp4", "/first/dhcp4/socket"); - testUnixSocketInfo("dhcp6", "/first/dhcp6/socket"); + testUnixSocketInfo("dhcp4", "first_socket4"); + testUnixSocketInfo("dhcp6", "first_socket6"); // After the shutdown the HTTP listener no longer exists. CtrlAgentProcessPtr process = getCtrlAgentProcess(); @@ -411,11 +434,11 @@ TEST_F(CtrlAgentControllerTest, noListenerChange) { " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp4/socket\"" + " \"socket-name\": \"second_socket6\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp6/socket\"" + " \"socket-name\": \"second_socket6\"" " }" " }" "}"; @@ -453,8 +476,8 @@ TEST_F(CtrlAgentControllerTest, noListenerChange) { EXPECT_EQ(8081, ctx->getHttpPort()); // The forwarding configuration should have been updated. - testUnixSocketInfo("dhcp4", "/second/dhcp4/socket"); - testUnixSocketInfo("dhcp6", "/second/dhcp6/socket"); + testUnixSocketInfo("dhcp4", "second_socket6"); + testUnixSocketInfo("dhcp6", "second_socket6"); CtrlAgentProcessPtr process = getCtrlAgentProcess(); ASSERT_TRUE(process); diff --git a/src/bin/agent/tests/get_config_unittest.cc b/src/bin/agent/tests/get_config_unittest.cc index 6f8cb93764..a0848415e8 100644 --- a/src/bin/agent/tests/get_config_unittest.cc +++ b/src/bin/agent/tests/get_config_unittest.cc @@ -8,11 +8,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include @@ -30,6 +32,8 @@ using namespace isc::data; using namespace isc::process; using namespace isc::hooks; using namespace isc::test; +using namespace isc::config; +using namespace isc::util; namespace { @@ -140,11 +144,13 @@ public: srv_.reset(new NakedAgentCfgMgr()); // Create fresh context. resetConfiguration(); + setSocketTestPath(); } ~CtrlAgentGetCfgTest() { resetConfiguration(); resetHooksPath(); + resetSocketPath(); } /// @brief Sets the Hooks path from which hooks can be loaded. @@ -160,6 +166,20 @@ public: HooksLibrariesParser::getHooksPath(true); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Parse and Execute configuration /// /// Parses a configuration and executes a configuration of the server. diff --git a/src/bin/agent/tests/parser_unittests.cc b/src/bin/agent/tests/parser_unittests.cc index 3432cff8a6..0884f10a8d 100644 --- a/src/bin/agent/tests/parser_unittests.cc +++ b/src/bin/agent/tests/parser_unittests.cc @@ -136,15 +136,15 @@ TEST(ParserTest, keywordAgent) { " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea4-ctrl-socket\"" + " \"socket-name\": \"kea4-ctrl-socket\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea6-ctrl-socket\"" + " \"socket-name\": \"kea6-ctrl-socket\"" " }," " \"d2\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea-ddns-ctrl-socket\"" + " \"socket-name\": \"kea-ddns-ctrl-socket\"" " }" " }," " \"hooks-libraries\": [" @@ -173,15 +173,15 @@ TEST(ParserTest, keywordSubAgent) { " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea4-ctrl-socket\"" + " \"socket-name\": \"kea4-ctrl-socket\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea6-ctrl-socket\"" + " \"socket-name\": \"kea6-ctrl-socket\"" " }," " \"d2\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea-ddns-ctrl-socket\"" + " \"socket-name\": \"kea-ddns-ctrl-socket\"" " }" " }," " \"hooks-libraries\": [" @@ -888,15 +888,15 @@ TEST_F(TrailingCommasTest, tests) { "Control-agent": { "control-sockets": { "d2": { - "socket-name": "/tmp/kea-dhcp-ddns-ctrl.sock", + "socket-name": "kea-dhcp-ddns-ctrl.sock", "socket-type": "unix", }, "dhcp4": { - "socket-name": "/tmp/kea-dhcp4-ctrl.sock", + "socket-name": "kea-dhcp4-ctrl.sock", "socket-type": "unix", }, "dhcp6": { - "socket-name": "/tmp/kea-dhcp6-ctrl.sock", + "socket-name": "kea-dhcp6-ctrl.sock", "socket-type": "unix", }, }, diff --git a/src/bin/agent/tests/testdata/get_config.json b/src/bin/agent/tests/testdata/get_config.json index 3117d66fc3..775732a155 100644 --- a/src/bin/agent/tests/testdata/get_config.json +++ b/src/bin/agent/tests/testdata/get_config.json @@ -23,21 +23,21 @@ }, "control-sockets": { "d2": { - "socket-name": "/tmp/kea-ddns-ctrl-socket", + "socket-name": "kea-ddns-ctrl-socket", "socket-type": "unix", "user-context": { "in-use": false } }, "dhcp4": { - "socket-name": "/tmp/kea4-ctrl-socket", + "socket-name": "kea4-ctrl-socket", "socket-type": "unix", "user-context": { "comment": "socket to DHCPv4 server" } }, "dhcp6": { - "socket-name": "/tmp/kea6-ctrl-socket", + "socket-name": "kea6-ctrl-socket", "socket-type": "unix" } }, diff --git a/src/bin/d2/tests/d2_cfg_mgr_unittests.cc b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc index 88da8c6812..c8fd3b1724 100644 --- a/src/bin/d2/tests/d2_cfg_mgr_unittests.cc +++ b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -26,6 +28,8 @@ using namespace isc; using namespace isc::d2; using namespace isc::hooks; using namespace isc::process; +using namespace isc::config; +using namespace isc::util; namespace { @@ -49,11 +53,13 @@ public: /// @brief Constructor D2CfgMgrTest():cfg_mgr_(new D2CfgMgr()), d2_params_() { resetHooksPath(); + resetSocketPath(); } /// @brief Destructor ~D2CfgMgrTest() { resetHooksPath(); + resetSocketPath(); } /// @brief Sets the Hooks path from which hooks can be loaded. @@ -69,6 +75,22 @@ public: HooksLibrariesParser::getHooksPath(true); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Configuration manager instance. D2CfgMgrPtr cfg_mgr_; @@ -477,6 +499,7 @@ TEST(D2CfgMgr, construction) { /// event. TEST_F(D2CfgMgrTest, fullConfig) { setHooksTestPath(); + setSocketTestPath(); // Create a configuration with all of application level parameters, plus // both the forward and reverse ddns managers. Both managers have two @@ -986,6 +1009,7 @@ TEST_F(D2CfgMgrTest, configPermutations) { /// @brief Tests comments. TEST_F(D2CfgMgrTest, comments) { + setSocketTestPath(); std::string config = "{ " "\"comment\": \"D2 config\" , " "\"ip-address\" : \"192.168.1.33\" , " diff --git a/src/bin/d2/tests/d2_command_unittest.cc b/src/bin/d2/tests/d2_command_unittest.cc index 1f3a84bd33..b901826f4d 100644 --- a/src/bin/d2/tests/d2_command_unittest.cc +++ b/src/bin/d2/tests/d2_command_unittest.cc @@ -10,12 +10,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -33,6 +35,7 @@ using namespace isc::d2; using namespace isc::data; using namespace isc::dhcp::test; using namespace isc::process; +using namespace isc::util; using namespace boost::asio; namespace ph = std::placeholders; @@ -122,12 +125,7 @@ public: /// Sets socket path to its default value. CtrlChannelD2Test() : server_(NakedD2Controller::instance()) { - const char* env = getenv("KEA_SOCKET_TEST_DIR"); - if (env) { - socket_path_ = string(env) + "/d2.sock"; - } else { - socket_path_ = sandbox.join("d2.sock"); - } + setSocketTestPath(); ::remove(socket_path_.c_str()); } @@ -143,6 +141,7 @@ public: // Reset command manager. CommandMgr::instance().deregisterAll(); CommandMgr::instance().setConnectionTimeout(TIMEOUT_DHCP_SERVER_RECEIVE_COMMAND); + resetSocketPath(); } /// @brief Returns pointer to the server's IO service. @@ -153,6 +152,23 @@ public: return (server_ ? d2Controller()->getIOService() : IOServicePtr()); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + socket_path_ = path + "/d2.sock"; + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Runs parser in DHCPDDNS mode /// /// @param config input configuration diff --git a/src/bin/d2/tests/get_config_unittest.cc b/src/bin/d2/tests/get_config_unittest.cc index 6eee45d066..c14b3e6087 100644 --- a/src/bin/d2/tests/get_config_unittest.cc +++ b/src/bin/d2/tests/get_config_unittest.cc @@ -8,12 +8,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -30,6 +32,7 @@ using namespace isc::data; using namespace isc::process; using namespace isc::test; using namespace isc::hooks; +using namespace isc::util; namespace { @@ -142,6 +145,7 @@ public: D2GetConfigTest() : rcode_(-1) { resetHooksPath(); + resetSocketPath(); srv_.reset(new D2CfgMgr()); // Enforce not verbose mode. Daemon::setVerbose(false); @@ -155,6 +159,7 @@ public: static_cast(remove(test_file_name.c_str())); resetConfiguration(); resetHooksPath(); + resetSocketPath(); } /// @brief Sets the Hooks path from which hooks can be loaded. @@ -170,6 +175,22 @@ public: HooksLibrariesParser::getHooksPath(true); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Parse and Execute configuration /// /// Parses a configuration and executes a configuration of the server. @@ -279,6 +300,7 @@ public: /// Test a configuration TEST_F(D2GetConfigTest, sample1) { setHooksTestPath(); + setSocketTestPath(); // get the sample1 configuration std::string sample1_file = string(CFG_EXAMPLES) + "/" + "sample1.json"; diff --git a/src/bin/d2/tests/testdata/get_config.json b/src/bin/d2/tests/testdata/get_config.json index ee3deccbb0..b5329233e8 100644 --- a/src/bin/d2/tests/testdata/get_config.json +++ b/src/bin/d2/tests/testdata/get_config.json @@ -1,7 +1,7 @@ { "DhcpDdns": { "control-socket": { - "socket-name": "/tmp/kea-ddns-ctrl-socket", + "socket-name": "kea-ddns-ctrl-socket", "socket-type": "unix" }, "dns-server-timeout": 1000, diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index 800fd4cd4a..f84623fb82 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -62,6 +63,7 @@ using namespace isc::dhcp; using namespace isc::dhcp::test; using namespace isc::hooks; using namespace isc::test; +using namespace isc::util; using namespace std; namespace { @@ -237,7 +239,7 @@ const char* PARSER_CONFIGS[] = { " ]," " \"control-socket\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea4-ctrl-socket\"," + " \"socket-name\": \"kea4-ctrl-socket\"," " \"user-context\": { \"comment\": \"Indirect comment\" }" " }," " \"shared-networks\": [ {" @@ -314,6 +316,7 @@ public: resetConfiguration(); resetHooksPath(); + Dhcpv4SrvTest::resetSocketPath(); } ~Dhcp4ParserTest() { @@ -325,6 +328,7 @@ public: static_cast(remove(UNLOAD_MARKER_FILE)); resetHooksPath(); + Dhcpv4SrvTest::resetSocketPath(); } /// @brief Sets the Hooks path from which hooks can be loaded. @@ -6984,6 +6988,7 @@ TEST_F(Dhcp4ParserTest, hostsDatabases) { // This test checks comments. Please keep it last. TEST_F(Dhcp4ParserTest, comments) { + Dhcpv4SrvTest::setSocketTestPath(); string config = PARSER_CONFIGS[6]; extractConfig(config); @@ -7081,7 +7086,7 @@ TEST_F(Dhcp4ParserTest, comments) { ASSERT_TRUE(socket->get("socket-type")); EXPECT_EQ("\"unix\"", socket->get("socket-type")->str()); ASSERT_TRUE(socket->get("socket-name")); - EXPECT_EQ("\"/tmp/kea4-ctrl-socket\"", socket->get("socket-name")->str()); + EXPECT_EQ("\"kea4-ctrl-socket\"", socket->get("socket-name")->str()); // Check control socket comment and user context. ConstElementPtr ctx_socket = socket->get("user-context"); diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index d99b7b3233..c0604d54bb 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -116,12 +117,7 @@ public: /// Sets socket path to its default value. CtrlChannelDhcpv4SrvTest() : interfaces_("\"*\"") { resetLogPath(); - const char* env = getenv("KEA_SOCKET_TEST_DIR"); - if (env) { - socket_path_ = string(env) + "/kea4.sock"; - } else { - socket_path_ = sandbox.join("kea4.sock"); - } + setSocketTestPath(); reset(); IfaceMgr::instance().setTestMode(false); IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces, @@ -131,6 +127,7 @@ public: /// @brief Destructor ~CtrlChannelDhcpv4SrvTest() { resetLogPath(); + Dhcpv4SrvTest::resetSocketPath(); LeaseMgrFactory::destroy(); StatsMgr::instance().removeAll(); @@ -160,6 +157,13 @@ public: LogConfigParser::getLogPath(true); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + Dhcpv4SrvTest::setSocketTestPath(explicit_path); + socket_path_ = UnixCommandConfig::getSocketPath() + "/kea4.sock"; + } + /// @brief Returns pointer to the server's IO service. /// /// @return Pointer to the server's IO service or null pointer if the server diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index b97776e72e..3f4970075f 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -2987,6 +2987,7 @@ class DBInitializer { void Dhcpv4SrvTest::checkConfigFiles() { + setSocketTestPath(); DBInitializer dbi; IfaceMgrTestConfig test_config(true); string path = CFG_EXAMPLES; diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc index acc62bea80..175f72ad9f 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ using namespace std; using namespace isc::asiolink; using namespace isc::data; using namespace isc::util; +using namespace isc::config; using namespace boost::posix_time; namespace isc { @@ -89,6 +91,7 @@ Dhcpv4SrvTest::Dhcpv4SrvTest() } Dhcpv4SrvTest::~Dhcpv4SrvTest() { + resetSocketPath(); // Make sure that we revert to default value CfgMgr::instance().clear(); @@ -101,6 +104,21 @@ Dhcpv4SrvTest::~Dhcpv4SrvTest() { MultiThreadingMgr::instance().apply(false, 0, 0); } +void +Dhcpv4SrvTest::setSocketTestPath(const std::string explicit_path /* = "" */) { + UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); +} + +void +Dhcpv4SrvTest::resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); +} + void Dhcpv4SrvTest::addPrlOption(Pkt4Ptr& pkt) { OptionUint8ArrayPtr option_prl = diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h index 269e6906f0..b4f9902a6f 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.h +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h @@ -384,6 +384,13 @@ public: /// Removes existing configuration. virtual ~Dhcpv4SrvTest(); + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + static void setSocketTestPath(const std::string explicit_path = ""); + + /// @brief Resets the socket path to the default. + static void resetSocketPath(); + /// @brief Add 'Parameter Request List' option to the packet. /// /// This function adds PRL option comprising the following option codes: diff --git a/src/bin/dhcp4/tests/get_config_unittest.cc b/src/bin/dhcp4/tests/get_config_unittest.cc index 781981d582..75f6855fec 100644 --- a/src/bin/dhcp4/tests/get_config_unittest.cc +++ b/src/bin/dhcp4/tests/get_config_unittest.cc @@ -2089,7 +2089,7 @@ const char* EXTRACTED_CONFIGS[] = { " }\n" " ],\n" " \"control-socket\": {\n" -" \"socket-name\": \"/tmp/kea4-ctrl-socket\",\n" +" \"socket-name\": \"kea4-ctrl-socket\",\n" " \"socket-type\": \"unix\",\n" " \"user-context\": {\n" " \"comment\": \"Indirect comment\"\n" @@ -11280,7 +11280,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ],\n" " \"control-socket\": {\n" -" \"socket-name\": \"/tmp/kea4-ctrl-socket\",\n" +" \"socket-name\": \"kea4-ctrl-socket\",\n" " \"socket-type\": \"unix\",\n" " \"user-context\": {\n" " \"comment\": \"Indirect comment\"\n" @@ -12721,10 +12721,12 @@ public: srv_.reset(new ControlledDhcpv4Srv(0)); // Create fresh context. resetConfiguration(); + Dhcpv4SrvTest::setSocketTestPath(); } ~Dhcp4GetConfigTest() { resetConfiguration(); + Dhcpv4SrvTest::resetSocketPath(); }; /// @brief Parse and Execute configuration diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index ab95e57e1e..75132ae696 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -318,7 +318,7 @@ const char* PARSER_CONFIGS[] = { " ]," " \"control-socket\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea6-ctrl-socket\"," + " \"socket-name\": \"kea6-ctrl-socket\"," " \"user-context\": { \"comment\": \"Indirect comment\" }" " }," " \"shared-networks\": [ {" @@ -7755,6 +7755,7 @@ TEST_F(Dhcp6ParserTest, hostsDatabases) { // This test checks comments. Please keep it last. TEST_F(Dhcp6ParserTest, comments) { + setSocketTestPath(); string config = PARSER_CONFIGS[9]; extractConfig(config); @@ -7864,7 +7865,7 @@ TEST_F(Dhcp6ParserTest, comments) { ASSERT_TRUE(socket->get("socket-type")); EXPECT_EQ("\"unix\"", socket->get("socket-type")->str()); ASSERT_TRUE(socket->get("socket-name")); - EXPECT_EQ("\"/tmp/kea6-ctrl-socket\"", socket->get("socket-name")->str()); + EXPECT_EQ("\"kea6-ctrl-socket\"", socket->get("socket-name")->str()); // Check control socket comment and user context. ConstElementPtr ctx_socket = socket->get("user-context"); diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index 21f8502062..708845991d 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -149,16 +150,12 @@ public: /// /// Sets socket path to its default value. CtrlChannelDhcpv6SrvTest() : interfaces_("\"*\"") { - const char* env = getenv("KEA_SOCKET_TEST_DIR"); - if (env) { - socket_path_ = string(env) + "/kea6.sock"; - } else { - socket_path_ = sandbox.join("/kea6.sock"); - } reset(); IfaceMgr::instance().setTestMode(false); IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces, IfaceMgr::instancePtr().get(), ph::_1)); + setSocketTestPath(); + socket_path_ = UnixCommandConfig::getSocketPath() + "/kea6.sock"; } /// @brief Destructor diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index 78cc98dea3..d89941218d 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -311,6 +311,7 @@ class DBInitializer { void Dhcpv6SrvTest::checkConfigFiles() { + setSocketTestPath(); DBInitializer dbi; IfaceMgrTestConfig test_config(true); string path = CFG_EXAMPLES; diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index b7c486fdb3..8c94ec09f3 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ using namespace isc::asiolink; using namespace isc::stats; using namespace isc::util; using namespace isc::process; +using namespace isc::config; using namespace boost::posix_time; namespace isc { @@ -40,6 +42,7 @@ BaseServerTest::BaseServerTest() { original_datadir_ = CfgMgr::instance().getDataDir(); CfgMgr::instance().getDataDir(true, TEST_DATA_BUILDDIR); resetLogPath(); + resetSocketPath(); } BaseServerTest::~BaseServerTest() { @@ -59,6 +62,7 @@ BaseServerTest::~BaseServerTest() { // Revert to unit test logging, in case the test reconfigured it. isc::log::initLogger(); resetLogPath(); + resetSocketPath(); } void @@ -72,6 +76,22 @@ BaseServerTest::resetLogPath() { LogConfigParser::getLogPath(true); } +void +BaseServerTest::setSocketTestPath(const std::string explicit_path /* = "" */) { + UnixCommandConfig::getSocketPath(true, + (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); +} + +void +BaseServerTest::resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); +} + Dhcpv6SrvTest::Dhcpv6SrvTest() : NakedDhcpv6SrvTest(), srv_(0), multi_threading_(false) { subnet_ = Subnet6::create(isc::asiolink::IOAddress("2001:db8:1::"), diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h index 938d67ca48..e8c8abdf64 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.h +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.h @@ -132,6 +132,13 @@ public: /// @brief Resets the log path to TEST_DATA_BUILDDIR. void resetLogPath(); + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + static void setSocketTestPath(const std::string explicit_path = ""); + + /// @brief Resets the socket path to the default. + static void resetSocketPath(); + private: /// @brief Holds the original data directory. diff --git a/src/bin/dhcp6/tests/get_config_unittest.cc b/src/bin/dhcp6/tests/get_config_unittest.cc index 6bc8471fa2..093f1e25e5 100644 --- a/src/bin/dhcp6/tests/get_config_unittest.cc +++ b/src/bin/dhcp6/tests/get_config_unittest.cc @@ -1983,7 +1983,7 @@ const char* EXTRACTED_CONFIGS[] = { " }\n" " ],\n" " \"control-socket\": {\n" -" \"socket-name\": \"/tmp/kea6-ctrl-socket\",\n" +" \"socket-name\": \"kea6-ctrl-socket\",\n" " \"socket-type\": \"unix\",\n" " \"user-context\": {\n" " \"comment\": \"Indirect comment\"\n" @@ -11045,7 +11045,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ],\n" " \"control-socket\": {\n" -" \"socket-name\": \"/tmp/kea6-ctrl-socket\",\n" +" \"socket-name\": \"kea6-ctrl-socket\",\n" " \"socket-type\": \"unix\",\n" " \"user-context\": {\n" " \"comment\": \"Indirect comment\"\n" @@ -12851,11 +12851,13 @@ public: // Reset configuration for each test. resetConfiguration(); + BaseServerTest::setSocketTestPath(); } ~Dhcp6GetConfigTest() { // Reset configuration database after each test. resetConfiguration(); + BaseServerTest::resetSocketPath(); }; /// @brief Parse and Execute configuration diff --git a/src/bin/keactrl/kea-ctrl-agent.conf.pre b/src/bin/keactrl/kea-ctrl-agent.conf.pre index f31096044d..9569919fce 100644 --- a/src/bin/keactrl/kea-ctrl-agent.conf.pre +++ b/src/bin/keactrl/kea-ctrl-agent.conf.pre @@ -66,15 +66,15 @@ "control-sockets": { "dhcp4": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, "dhcp6": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, "d2": { "socket-type": "unix", - "socket-name": "/tmp/kea-ddns-ctrl-socket" + "socket-name": "kea-ddns-ctrl-socket" } }, diff --git a/src/bin/keactrl/kea-dhcp-ddns.conf.pre b/src/bin/keactrl/kea-dhcp-ddns.conf.pre index 0d7f11536e..867f018029 100644 --- a/src/bin/keactrl/kea-dhcp-ddns.conf.pre +++ b/src/bin/keactrl/kea-dhcp-ddns.conf.pre @@ -23,7 +23,7 @@ "port": 53001, "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea-ddns-ctrl-socket" + "socket-name": "kea-ddns-ctrl-socket" }, "tsig-keys": [], "forward-ddns" : {}, diff --git a/src/bin/keactrl/kea-dhcp4.conf.pre b/src/bin/keactrl/kea-dhcp4.conf.pre index 06070508fc..07511ccef8 100644 --- a/src/bin/keactrl/kea-dhcp4.conf.pre +++ b/src/bin/keactrl/kea-dhcp4.conf.pre @@ -49,7 +49,7 @@ // more. For detailed description, see Sections 8.8, 16 and 15. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Use Memfile lease database backend to store leases in a CSV file. diff --git a/src/bin/keactrl/kea-dhcp6.conf.pre b/src/bin/keactrl/kea-dhcp6.conf.pre index 183866d58b..221381e68f 100644 --- a/src/bin/keactrl/kea-dhcp6.conf.pre +++ b/src/bin/keactrl/kea-dhcp6.conf.pre @@ -43,7 +43,7 @@ // description, see Sections 9.12, 16 and 15. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Use Memfile lease database backend to store leases in a CSV file. diff --git a/src/bin/keactrl/kea-netconf.conf.pre b/src/bin/keactrl/kea-netconf.conf.pre index bc7b3926f4..b559604ecd 100644 --- a/src/bin/keactrl/kea-netconf.conf.pre +++ b/src/bin/keactrl/kea-netconf.conf.pre @@ -30,13 +30,13 @@ "dhcp4": { "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" } }, "dhcp6": { "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" } } }, diff --git a/src/bin/netconf/tests/testdata/get_config.json b/src/bin/netconf/tests/testdata/get_config.json index e2f5d31486..1875333698 100644 --- a/src/bin/netconf/tests/testdata/get_config.json +++ b/src/bin/netconf/tests/testdata/get_config.json @@ -6,7 +6,7 @@ "dhcp4": { "boot-update": true, "control-socket": { - "socket-name": "/tmp/kea4-ctrl-socket", + "socket-name": "kea4-ctrl-socket", "socket-type": "unix", "socket-url": "http://127.0.0.1:8000/" }, diff --git a/src/lib/config/Makefile.am b/src/lib/config/Makefile.am index d5120626d4..8bd20b3e5c 100644 --- a/src/lib/config/Makefile.am +++ b/src/lib/config/Makefile.am @@ -2,6 +2,8 @@ SUBDIRS = . tests AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) +control_socket_dir = @runstatedir@/@PACKAGE@ +AM_CPPFLAGS += -DCONTROL_SOCKET_DIR=\"$(control_socket_dir)\" AM_CXXFLAGS = $(KEA_CXXFLAGS) @@ -17,6 +19,7 @@ libkea_cfgclient_la_SOURCES += timeouts.h libkea_cfgclient_la_SOURCES += cmd_http_listener.cc cmd_http_listener.h libkea_cfgclient_la_SOURCES += cmd_response_creator.cc cmd_response_creator.h libkea_cfgclient_la_SOURCES += cmd_response_creator_factory.h +libkea_cfgclient_la_SOURCES += unix_command_config.cc unix_command_config.h libkea_cfgclient_la_LIBADD = $(top_builddir)/src/lib/http/libkea-http.la libkea_cfgclient_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la diff --git a/src/lib/config/command_mgr.cc b/src/lib/config/command_mgr.cc index cf21a697ab..8093f44068 100644 --- a/src/lib/config/command_mgr.cc +++ b/src/lib/config/command_mgr.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -544,7 +545,11 @@ CommandMgrImpl::openCommandSocket(const isc::data::ConstElementPtr& socket_info) isc_throw(BadSocketInfo, "'socket-name' parameter expected to be a string"); } - socket_name_ = name->stringValue(); + try { + socket_name_ = UnixCommandConfig::validatePath(name->stringValue()); + } catch (const std::exception& ex) { + isc_throw(BadSocketInfo, "'socket-name' is invalid: " << ex.what()); + } // First let's open lock file. std::string lock_name = getLockName(); @@ -584,6 +589,8 @@ CommandMgrImpl::openCommandSocket(const isc::data::ConstElementPtr& socket_info) doAccept(); } catch (const std::exception& ex) { + close(lock_fd); + static_cast(::remove(getLockName().c_str())); isc_throw(SocketError, ex.what()); } } diff --git a/src/lib/config/tests/Makefile.am b/src/lib/config/tests/Makefile.am index 547d0ef132..138ce1d09b 100644 --- a/src/lib/config/tests/Makefile.am +++ b/src/lib/config/tests/Makefile.am @@ -4,6 +4,8 @@ AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/config/tests\" AM_CPPFLAGS += -DTEST_CA_DIR=\"$(srcdir)/../../asiolink/testutils/ca\" +control_socket_dir = @runstatedir@/@PACKAGE@ +AM_CPPFLAGS += -DCONTROL_SOCKET_DIR=\"$(control_socket_dir)\" AM_CXXFLAGS = $(KEA_CXXFLAGS) @@ -24,6 +26,7 @@ run_unittests_SOURCES += command_mgr_unittests.cc run_unittests_SOURCES += cmd_http_listener_unittests.cc run_unittests_SOURCES += cmd_response_creator_unittests.cc run_unittests_SOURCES += cmd_response_creator_factory_unittests.cc +run_unittests_SOURCES += unix_command_config_unittests.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) @@ -38,6 +41,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la run_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la diff --git a/src/lib/config/tests/command_mgr_unittests.cc b/src/lib/config/tests/command_mgr_unittests.cc index 492bcfa825..3856971716 100644 --- a/src/lib/config/tests/command_mgr_unittests.cc +++ b/src/lib/config/tests/command_mgr_unittests.cc @@ -12,11 +12,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -24,6 +26,7 @@ using namespace isc::asiolink; using namespace isc::config; using namespace isc::data; using namespace isc::hooks; +using namespace isc::util; using namespace std; // Test class for Command Manager @@ -34,6 +37,7 @@ public: /// Default constructor CommandMgrTest() : io_service_(new IOService()) { + resetSocketPath(); CommandMgr::instance().setIOService(io_service_); @@ -55,19 +59,24 @@ public: CommandMgr::instance().deregisterAll(); CommandMgr::instance().closeCommandSocket(); resetCalloutIndicators(); + resetSocketPath(); } - /// @brief Returns socket path (using either hardcoded path or env variable) - /// @return path to the unix socket - std::string getSocketPath() { - std::string socket_path; - const char* env = getenv("KEA_SOCKET_TEST_DIR"); - if (env) { - socket_path = std::string(env) + "/test-socket"; - } else { - socket_path = sandbox.join("test-socket"); - } - return (socket_path); + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the hooks path. + void setSocketTestPath(const std::string explicit_path = "") { + UnixCommandConfig::getSocketPath(true, + (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); } /// @brief Resets indicators related to callout invocation. @@ -430,6 +439,7 @@ TEST_F(CommandMgrTest, delegateListCommands) { // This test verifies that a Unix socket can be opened properly and that input // parameters (socket-type and socket-name) are verified. TEST_F(CommandMgrTest, unixCreate) { + setSocketTestPath(); // Null pointer is obviously a bad idea. EXPECT_THROW(CommandMgr::instance().openCommandSocket(ConstElementPtr()), isc::config::BadSocketInfo); @@ -448,7 +458,7 @@ TEST_F(CommandMgrTest, unixCreate) { EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info), isc::config::BadSocketInfo); - socket_info->set("socket-name", Element::create(getSocketPath())); + socket_info->set("socket-name", Element::create("test-socket")); EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info)); EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0); @@ -458,8 +468,9 @@ TEST_F(CommandMgrTest, unixCreate) { // This test checks that when unix path is too long, the socket cannot be opened. TEST_F(CommandMgrTest, unixCreateTooLong) { + setSocketTestPath(); ElementPtr socket_info = Element::fromJSON("{ \"socket-type\": \"unix\"," - "\"socket-name\": \"/tmp/toolongtoolongtoolongtoolongtoolongtoolong" + "\"socket-name\": \"toolongtoolongtoolongtoolongtoolongtoolong" "toolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolong" "\" }"); @@ -545,10 +556,11 @@ TEST_F(CommandMgrTest, commandProcessedHookReplaceResponse) { // Verifies that a socket cannot be concurrently opened more than once. TEST_F(CommandMgrTest, exclusiveOpen) { + setSocketTestPath(); // Pass in valid parameters. ElementPtr socket_info = Element::createMap(); socket_info->set("socket-type", Element::create("unix")); - socket_info->set("socket-name", Element::create(getSocketPath())); + socket_info->set("socket-name", Element::create("test-socket")); EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info)); EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0); diff --git a/src/lib/config/tests/unix_command_config_unittests.cc b/src/lib/config/tests/unix_command_config_unittests.cc new file mode 100644 index 0000000000..f5b3f70c3e --- /dev/null +++ b/src/lib/config/tests/unix_command_config_unittests.cc @@ -0,0 +1,65 @@ +// Copyright (C) 2025 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include + +using namespace isc; +using namespace isc::config; +using namespace isc::test; +using namespace isc::util; +using namespace std; + +namespace { + +/// @brief Test fixture for UNIX control socket configuration. +class UnixCommandConfigTest : public ::testing::Test { +public: + /// @brief Constructor. + UnixCommandConfigTest() { + setSocketTestPath(); + } + + /// @brief Destructor. + ~UnixCommandConfigTest() { + resetSocketPath(); + } + + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the hooks path. + void setSocketTestPath(const std::string explicit_path = "") { + UnixCommandConfig::getSocketPath(true, + (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } +}; + +TEST(SocketPathTest, socketDir) { + EnvVarWrapper env("KEA_CONTROL_SOCKET_DIR"); + env.setValue(""); + + auto path = UnixCommandConfig::getSocketPath(true); + ASSERT_EQ(path, std::string(CONTROL_SOCKET_DIR)); + + env.setValue(TEST_DATA_BUILDDIR); + path = UnixCommandConfig::getSocketPath(true); + ASSERT_EQ(path, std::string(TEST_DATA_BUILDDIR)); +} + +} // end of anonymous namespace diff --git a/src/lib/config/unix_command_config.cc b/src/lib/config/unix_command_config.cc new file mode 100644 index 0000000000..7c7d5b3fcc --- /dev/null +++ b/src/lib/config/unix_command_config.cc @@ -0,0 +1,75 @@ +// Copyright (C) 2025 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include + +using namespace isc; +using namespace isc::config; +using namespace isc::util::file; +using namespace std; + +namespace isc { +namespace config { + +namespace { + // Singleton PathChecker to set and hold valid unix socket path. + PathCheckerPtr socket_path_checker_; + +}; + +const mode_t UnixCommandConfig::DEFAULT_SOCKET_PATH_PERMS = (S_IRWXU | S_IRGRP | S_IXGRP); + +mode_t UnixCommandConfig::socket_path_perms_ = UnixCommandConfig::DEFAULT_SOCKET_PATH_PERMS; + +std::string +UnixCommandConfig::getSocketPath(bool reset /* = false */, + const std::string explicit_path /* = "" */) { + if (!socket_path_checker_ || reset) { + socket_path_checker_.reset(new PathChecker(CONTROL_SOCKET_DIR, + "KEA_CONTROL_SOCKET_DIR")); + if (!explicit_path.empty()) { + socket_path_checker_->getPath(true, explicit_path); + } + } + + return (socket_path_checker_->getPath()); +} + +std::string +UnixCommandConfig::validatePath(const std::string socket_path, + bool enforce /* = true */) { + if (!socket_path_checker_) { + getSocketPath(); + } + + auto valid_path = socket_path_checker_->validatePath(socket_path, enforce); + if (enforce && !(socket_path_checker_->pathHasPermissions(socket_path_perms_))) { + isc_throw (BadValue, + "socket path:" << socket_path_checker_->getPath() + << " does not exist or does not have permssions = " + << std::oct << socket_path_perms_); + } + + return (valid_path); +} + +mode_t +UnixCommandConfig::getSocketPathPerms() { + return (socket_path_perms_); +} + +void +UnixCommandConfig::setSocketPathPerms(mode_t perms + /* = DEFAULT_SOCKET_PATH_PERMS */) { + socket_path_perms_ = perms; +} + +} // end of isc::config +} // end of isc diff --git a/src/lib/config/unix_command_config.h b/src/lib/config/unix_command_config.h new file mode 100644 index 0000000000..6c8da273ce --- /dev/null +++ b/src/lib/config/unix_command_config.h @@ -0,0 +1,72 @@ +// Copyright (C) 2025 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef UNIX_COMMAND_CONFIG_H +#define UNIX_COMMAND_CONFIG_H + +#include +#include + +namespace isc { +namespace config { + +/// @brief UNIX command config aka UNIX control socket info class. +class UnixCommandConfig { +public: + /// @brief Defines the default permissions for unix socket parent directory. + static const mode_t DEFAULT_SOCKET_PATH_PERMS; + + /// @brief Stores the default permissions for unix socket parent directory. + static mode_t socket_path_perms_; + + /// @brief Constructor. + UnixCommandConfig() = default; + + /// @brief Virtual destructor. + ~UnixCommandConfig() = default; + + /// @brief Fetches the supported control socket path. + /// + /// The first call to this function with no arguments will set the default + /// path to either the value of CONTROL_SOCKET_DIR or the environment + /// variable KEA_CONTROL_SOCKET_DIR if it is defined. Subsequent calls with + /// no arguments will simply return this value. + /// + /// @param reset recalculate when true, defaults to false. + /// @param explicit_path set the default socket path to this value. This is + /// for testing purposes only. + /// + /// @return String containing the default socket path. + static std::string getSocketPath(bool reset = false, + const std::string explicit_path = ""); + + /// @brief Validates a path against the supported path for unix control + /// sockets. + /// + /// @param socket_path path to validate. + /// @param enforce enables validation against the supported path and + /// permissions. + /// If false simply returns the input path. + /// + /// @return validated path + static std::string validatePath(const std::string socket_path, + bool enforce = true); + + /// @brief Fetches the required socket path permissions mask + /// + /// @return permissions mask + static mode_t getSocketPathPerms(); + + /// @brief Sets the required socket path permissions mask + /// + /// This is for testing purposes only. + /// @param perms permissions mask to use + static void setSocketPathPerms(mode_t perms = DEFAULT_SOCKET_PATH_PERMS); +}; + +} // end of isc::config namespace +} // end of isc namespace +#endif diff --git a/src/lib/util/filesystem.cc b/src/lib/util/filesystem.cc index 3b01ee6445..cf7a506f2a 100644 --- a/src/lib/util/filesystem.cc +++ b/src/lib/util/filesystem.cc @@ -51,6 +51,21 @@ exists(string const& path) { return (::stat(path.c_str(), &statbuf) == 0); } +mode_t +getPermissions(const std::string path) { + struct stat statbuf; + if (::stat(path.c_str(), &statbuf) < 0) { + return (0); + } + + return (statbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); +} + +bool +hasPermissions(const std::string path, const mode_t& permissions) { + return (getPermissions(path) == permissions); +} + bool isDir(string const& path) { struct stat statbuf; @@ -274,6 +289,11 @@ PathChecker::validatePath(const std::string input_path_str, return (valid_path); } +bool +PathChecker::pathHasPermissions(mode_t permissions) { + return(hasPermissions(path_, permissions)); +} + } // namespace file } // namespace util } // namespace isc diff --git a/src/lib/util/filesystem.h b/src/lib/util/filesystem.h index 951dd622db..45be0c76a0 100644 --- a/src/lib/util/filesystem.h +++ b/src/lib/util/filesystem.h @@ -41,6 +41,22 @@ exists(const std::string& path); bool isDir(const std::string& path); +/// @brief Fetches the file permissions mask. +/// +/// @param path The path being checked. +/// @return File permissios mask or 0 if the path does not exist. +mode_t +getPermissions(const std::string path); + +/// @brief Check if there if file or directory has the given permissions. +/// +/// @param path The path being checked. +/// @param permissions mask of expected permissions. +/// +/// @return True if the path points to a file or a directory, false otherwise. +bool +hasPermissions(const std::string path, const mode_t& permissions); + /// @brief Check if there is a file at the given path. /// /// @param path The path being checked. @@ -201,6 +217,13 @@ public: std::string validatePath(const std::string input_path_str, bool enforce_path = true) const; + /// @brief Tests that the supported path has the given permissions. + /// + /// @param permissions mode_t mask of required permissions. + /// @return True if the path's permissions exactly match the permissions + /// parameter. + bool pathHasPermissions(mode_t permissions); + /// @brief Fetches the default path. std::string getDefaultPath() const { return (default_path_); diff --git a/src/lib/util/tests/filesystem_unittests.cc b/src/lib/util/tests/filesystem_unittests.cc index 40723726c1..ca6b1540f7 100644 --- a/src/lib/util/tests/filesystem_unittests.cc +++ b/src/lib/util/tests/filesystem_unittests.cc @@ -424,4 +424,15 @@ TEST(PathChecker, validatePathEnforcePathFalse) { } } +/// @brief Check hasPermissions. +TEST_F(FileUtilTest, hasPermissions) { + const std::string path = ABS_SRCDIR "/filesystem_unittests.cc"; + ASSERT_TRUE(isFile(path)); + mode_t current_permissions = getPermissions(path); + EXPECT_TRUE(hasPermissions(path, current_permissions)); + current_permissions = ~current_permissions; + EXPECT_FALSE(hasPermissions(path, current_permissions)); +} + + } // namespace