From 05fd34e2ee3bc768d7a076786b165de68fc5597b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 4 Nov 2021 12:48:59 +0100 Subject: [PATCH 01/97] ITL: add CheckCommand icinga4win-api --- itl/command-icinga.conf | 4 + lib/methods/CMakeLists.txt | 1 + lib/methods/icinga4winapichecktask.cpp | 137 +++++++++++++++++++++++++ lib/methods/icinga4winapichecktask.hpp | 30 ++++++ lib/methods/methods-itl.conf | 5 + 5 files changed, 177 insertions(+) create mode 100644 lib/methods/icinga4winapichecktask.cpp create mode 100644 lib/methods/icinga4winapichecktask.hpp diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index 206324a4ea5..eaacf5018ec 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -39,3 +39,7 @@ object CheckCommand "exception" { object CheckCommand "sleep" { import "sleep-check-command" } + +object CheckCommand "icinga4win-api" { + import "icinga4win-api-check-command" +} diff --git a/lib/methods/CMakeLists.txt b/lib/methods/CMakeLists.txt index 9fde9fddb35..10f736e6e98 100644 --- a/lib/methods/CMakeLists.txt +++ b/lib/methods/CMakeLists.txt @@ -9,6 +9,7 @@ set(methods_SOURCES dummychecktask.cpp dummychecktask.hpp exceptionchecktask.cpp exceptionchecktask.hpp icingachecktask.cpp icingachecktask.hpp + icinga4winapichecktask.cpp icinga4winapichecktask.hpp nullchecktask.cpp nullchecktask.hpp nulleventtask.cpp nulleventtask.hpp pluginchecktask.cpp pluginchecktask.hpp diff --git a/lib/methods/icinga4winapichecktask.cpp b/lib/methods/icinga4winapichecktask.cpp new file mode 100644 index 00000000000..b63dac1495a --- /dev/null +++ b/lib/methods/icinga4winapichecktask.cpp @@ -0,0 +1,137 @@ +/* Icinga 2 | (c) 2021 Icinga GmbH | GPLv2+ */ + +#ifndef _WIN32 +# include +#endif /* _WIN32 */ +#include "methods/icinga4winapichecktask.hpp" +#include "icinga/icingaapplication.hpp" +#include "icinga/pluginutility.hpp" +#include "base/utility.hpp" +#include "base/perfdatavalue.hpp" +#include "base/convert.hpp" +#include "base/function.hpp" +#include "base/io-engine.hpp" +#include "base/json.hpp" +#include "base/logger.hpp" +#include "base/tcpsocket.hpp" +#include "base/tlsstream.hpp" +#include +#include +#include + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, Icinga4WinApiCheck, &Icinga4WinApiCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +void Icinga4WinApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + using namespace boost::asio; + using namespace boost::beast; + using namespace boost::beast::http; + + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + MacroProcessor::ResolverList resolvers; + + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); + + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("command", command); + resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); + + String psCommand = MacroProcessor::ResolveMacros("$icinga4win_api_command_name$", resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + Dictionary::Ptr arguments = MacroProcessor::ResolveMacros("$icinga4win_api_arguments$", resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + Dictionary::Ptr params = new Dictionary(); + + if (arguments) { + ObjectLock oLock (arguments); + Array::Ptr dummy = new Array(); + + for (auto& kv : arguments) { + Array::Ptr arg = MacroProcessor::ResolveArguments( + dummy, new Dictionary({{kv.first, kv.second}}), resolvers, + checkable->GetLastCheckResult(), resolvedMacros, useResolvedMacros + ); + + switch (arg ? arg->GetLength() : 0) { + case 0: + continue; + case 1: + params->Set(arg->Get(0), true); + break; + case 2: + params->Set(arg->Get(0), arg->Get(1)); + break; + default: { + auto k (arg->Get(0)); + + arg->Remove(0); + params->Set(k, arg); + } + } + } + } + + if (resolvedMacros && !useResolvedMacros) + return; + + ssl::context ctx (ssl::context::tls); + AsioTlsStream conn (IoEngine::Get().GetIoContext(), ctx); + request req; + flat_buffer buf; + response resp; + + req.method(verb::post); + req.target("/v1/checker?command=" + psCommand); + req.set(field::content_type, "application/json"); + req.body() = JsonEncode(params); + + Connect(conn.lowest_layer(), "127.0.0.1", "5668"); + conn.next_layer().handshake(conn.next_layer().client); + + double start = Utility::GetTime(); + + write(conn, req); + conn.flush(); + read(conn, buf, resp); + + double end = Utility::GetTime(); + + Dictionary::Ptr result = Dictionary::Ptr(JsonDecode(resp.body()))->Get(psCommand); + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + ProcessResult pr; + pr.PID = -1; + pr.Output = result->Get("checkresult") + " |" + Array::Ptr(result->Get("perfdata"))->Join(""); + pr.ExecutionStart = start; + pr.ExecutionEnd = end; + pr.ExitStatus = result->Get("exitcode"); + + Checkable::ExecuteCommandProcessFinishedHandler(command->GetName(), pr); + } else { + cr->SetOutput(result->Get("checkresult")); + cr->SetPerformanceData(result->Get("perfdata")); + cr->SetState((ServiceState)(int)result->Get("exitcode")); + cr->SetExitStatus(result->Get("exitcode")); + cr->SetExecutionStart(start); + cr->SetExecutionEnd(end); + cr->SetCommand(command->GetName()); + + checkable->ProcessCheckResult(cr); + } +} diff --git a/lib/methods/icinga4winapichecktask.hpp b/lib/methods/icinga4winapichecktask.hpp new file mode 100644 index 00000000000..10824fe653e --- /dev/null +++ b/lib/methods/icinga4winapichecktask.hpp @@ -0,0 +1,30 @@ +/* Icinga 2 | (c) 2021 Icinga GmbH | GPLv2+ */ + +#ifndef ICINGA4WINAPICHECKTASK_H +#define ICINGA4WINAPICHECKTASK_H + +#include "methods/i2-methods.hpp" +#include "icinga/service.hpp" +#include "base/dictionary.hpp" + +namespace icinga +{ + +/** + * Executes checks via local Icinga for Windows API. + * + * @ingroup methods + */ +class Icinga4WinApiCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + Icinga4WinApiCheckTask(); +}; + +} + +#endif /* ICINGA4WINAPICHECKTASK_H */ diff --git a/lib/methods/methods-itl.conf b/lib/methods/methods-itl.conf index f9126f7ca24..07674f50565 100644 --- a/lib/methods/methods-itl.conf +++ b/lib/methods/methods-itl.conf @@ -43,6 +43,10 @@ System.assert(Internal.run_with_activation_context(function() { execute = NullCheck } + template CheckCommand "icinga4win-api-check-command" use (Icinga4WinApiCheck = Internal.Icinga4WinApiCheck) { + execute = Icinga4WinApiCheck + } + template EventCommand "null-event-command" use (NullEvent = Internal.NullEvent) { execute = NullEvent } @@ -64,6 +68,7 @@ System.assert(Internal.run_with_activation_context(function() { var methods = [ "IcingaCheck", + "Icinga4WinApiCheck", "ClusterCheck", "ClusterZoneCheck", "PluginCheck", From 7b1598be4243fdb376c15de483f93f3d44b4985b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 4 Nov 2021 12:54:16 +0100 Subject: [PATCH 02/97] ITL: add checkable templates for CheckCommand icinga4win-api --- itl/command-icinga.conf | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index eaacf5018ec..b47df3823fb 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -43,3 +43,27 @@ object CheckCommand "sleep" { object CheckCommand "icinga4win-api" { import "icinga4win-api-check-command" } + +var icinga4win_api_template = function() { + var my_arguments = get_check_command(check_command).arguments + var psbase_arguments = get_check_command("PowerShell Base").arguments.keys() + [ "-C" ] + + vars.icinga4win_api_command_name = check_command + vars.icinga4win_api_arguments = {} + + for (k => v in my_arguments) { + if (k !in psbase_arguments) { + vars.icinga4win_api_arguments[k] = v + } + } + + check_command = "icinga4win-api" +} + +template Host "icinga4win-api" use (icinga4win_api_template) { + icinga4win_api_template.call(this) +} + +template Service "icinga4win-api" use (icinga4win_api_template) { + icinga4win_api_template.call(this) +} From 648c0220d3d99f5092e558c97d3be2c91e424df7 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 11:59:57 +0100 Subject: [PATCH 03/97] Revert "ITL: add checkable templates for CheckCommand icinga4win-api" This reverts commit 0df44b8bce6ba615acddf2908e10cbd0779199b5. --- itl/command-icinga.conf | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index b47df3823fb..eaacf5018ec 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -43,27 +43,3 @@ object CheckCommand "sleep" { object CheckCommand "icinga4win-api" { import "icinga4win-api-check-command" } - -var icinga4win_api_template = function() { - var my_arguments = get_check_command(check_command).arguments - var psbase_arguments = get_check_command("PowerShell Base").arguments.keys() + [ "-C" ] - - vars.icinga4win_api_command_name = check_command - vars.icinga4win_api_arguments = {} - - for (k => v in my_arguments) { - if (k !in psbase_arguments) { - vars.icinga4win_api_arguments[k] = v - } - } - - check_command = "icinga4win-api" -} - -template Host "icinga4win-api" use (icinga4win_api_template) { - icinga4win_api_template.call(this) -} - -template Service "icinga4win-api" use (icinga4win_api_template) { - icinga4win_api_template.call(this) -} From 99853adf5707d14db070ae596734e129f77c19b1 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 12:02:17 +0100 Subject: [PATCH 04/97] WIP --- itl/command-icinga.conf | 2 +- lib/methods/icinga4winapichecktask.cpp | 4 ++-- lib/methods/icinga4winapichecktask.hpp | 4 ++-- lib/methods/methods-itl.conf | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index eaacf5018ec..b32f252e2e4 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -41,5 +41,5 @@ object CheckCommand "sleep" { } object CheckCommand "icinga4win-api" { - import "icinga4win-api-check-command" + import "ifw-api-check-command" } diff --git a/lib/methods/icinga4winapichecktask.cpp b/lib/methods/icinga4winapichecktask.cpp index b63dac1495a..d03cbc67ed4 100644 --- a/lib/methods/icinga4winapichecktask.cpp +++ b/lib/methods/icinga4winapichecktask.cpp @@ -21,9 +21,9 @@ using namespace icinga; -REGISTER_FUNCTION_NONCONST(Internal, Icinga4WinApiCheck, &Icinga4WinApiCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, IfwApiCheck, &IfwApiCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); -void Icinga4WinApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, +void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { using namespace boost::asio; diff --git a/lib/methods/icinga4winapichecktask.hpp b/lib/methods/icinga4winapichecktask.hpp index 10824fe653e..8031e26924b 100644 --- a/lib/methods/icinga4winapichecktask.hpp +++ b/lib/methods/icinga4winapichecktask.hpp @@ -15,14 +15,14 @@ namespace icinga * * @ingroup methods */ -class Icinga4WinApiCheckTask +class IfwApiCheckTask { public: static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); private: - Icinga4WinApiCheckTask(); + IfwApiCheckTask(); }; } diff --git a/lib/methods/methods-itl.conf b/lib/methods/methods-itl.conf index 07674f50565..6249692815a 100644 --- a/lib/methods/methods-itl.conf +++ b/lib/methods/methods-itl.conf @@ -43,8 +43,8 @@ System.assert(Internal.run_with_activation_context(function() { execute = NullCheck } - template CheckCommand "icinga4win-api-check-command" use (Icinga4WinApiCheck = Internal.Icinga4WinApiCheck) { - execute = Icinga4WinApiCheck + template CheckCommand "ifw-api-check-command" use (IfwApiCheck = Internal.IfwApiCheck) { + execute = IfwApiCheck } template EventCommand "null-event-command" use (NullEvent = Internal.NullEvent) { @@ -68,7 +68,7 @@ System.assert(Internal.run_with_activation_context(function() { var methods = [ "IcingaCheck", - "Icinga4WinApiCheck", + "IfwApiCheck", "ClusterCheck", "ClusterZoneCheck", "PluginCheck", From 884fc62c8eef0d6e7ad9b7ce2aabca35e82a9f4a Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 12:03:02 +0100 Subject: [PATCH 05/97] WIP --- itl/command-icinga.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index b32f252e2e4..bac2e5feb50 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -40,6 +40,6 @@ object CheckCommand "sleep" { import "sleep-check-command" } -object CheckCommand "icinga4win-api" { +object CheckCommand "ifw-api" { import "ifw-api-check-command" } From 35708c8cf58b0c0c1cb2749f4fc30f132cbe7f38 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 12:09:36 +0100 Subject: [PATCH 06/97] WIP --- lib/methods/CMakeLists.txt | 2 +- lib/methods/{icinga4winapichecktask.cpp => ifwapichecktask.cpp} | 2 +- lib/methods/{icinga4winapichecktask.hpp => ifwapichecktask.hpp} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename lib/methods/{icinga4winapichecktask.cpp => ifwapichecktask.cpp} (98%) rename lib/methods/{icinga4winapichecktask.hpp => ifwapichecktask.hpp} (100%) diff --git a/lib/methods/CMakeLists.txt b/lib/methods/CMakeLists.txt index 10f736e6e98..a7c3090b894 100644 --- a/lib/methods/CMakeLists.txt +++ b/lib/methods/CMakeLists.txt @@ -9,7 +9,7 @@ set(methods_SOURCES dummychecktask.cpp dummychecktask.hpp exceptionchecktask.cpp exceptionchecktask.hpp icingachecktask.cpp icingachecktask.hpp - icinga4winapichecktask.cpp icinga4winapichecktask.hpp + ifwapichecktask.cpp ifwapichecktask.hpp nullchecktask.cpp nullchecktask.hpp nulleventtask.cpp nulleventtask.hpp pluginchecktask.cpp pluginchecktask.hpp diff --git a/lib/methods/icinga4winapichecktask.cpp b/lib/methods/ifwapichecktask.cpp similarity index 98% rename from lib/methods/icinga4winapichecktask.cpp rename to lib/methods/ifwapichecktask.cpp index d03cbc67ed4..938f2d39b4e 100644 --- a/lib/methods/icinga4winapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -3,7 +3,7 @@ #ifndef _WIN32 # include #endif /* _WIN32 */ -#include "methods/icinga4winapichecktask.hpp" +#include "methods/ifwapichecktask.hpp" #include "icinga/icingaapplication.hpp" #include "icinga/pluginutility.hpp" #include "base/utility.hpp" diff --git a/lib/methods/icinga4winapichecktask.hpp b/lib/methods/ifwapichecktask.hpp similarity index 100% rename from lib/methods/icinga4winapichecktask.hpp rename to lib/methods/ifwapichecktask.hpp From bdb315f1c23d143c0d99be75cffc7e1c24ee003c Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 12:11:22 +0100 Subject: [PATCH 07/97] WIP --- lib/methods/icingachecktask.hpp | 7 ++----- lib/methods/ifwapichecktask.cpp | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/methods/icingachecktask.hpp b/lib/methods/icingachecktask.hpp index 93def628d76..7dcd29da07e 100644 --- a/lib/methods/icingachecktask.hpp +++ b/lib/methods/icingachecktask.hpp @@ -1,7 +1,6 @@ -/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ +/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */ -#ifndef ICINGACHECKTASK_H -#define ICINGACHECKTASK_H +#pragma once #include "methods/i2-methods.hpp" #include "icinga/service.hpp" @@ -25,5 +24,3 @@ class IcingaCheckTask }; } - -#endif /* ICINGACHECKTASK_H */ diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 938f2d39b4e..86abed9e67d 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -1,4 +1,4 @@ -/* Icinga 2 | (c) 2021 Icinga GmbH | GPLv2+ */ +/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */ #ifndef _WIN32 # include From 281edad99541dd7af07c68fe2a07e72588ead9ec Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 12:12:19 +0100 Subject: [PATCH 08/97] WIP --- lib/methods/icingachecktask.hpp | 7 +++++-- lib/methods/ifwapichecktask.hpp | 7 ++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/methods/icingachecktask.hpp b/lib/methods/icingachecktask.hpp index 7dcd29da07e..93def628d76 100644 --- a/lib/methods/icingachecktask.hpp +++ b/lib/methods/icingachecktask.hpp @@ -1,6 +1,7 @@ -/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ -#pragma once +#ifndef ICINGACHECKTASK_H +#define ICINGACHECKTASK_H #include "methods/i2-methods.hpp" #include "icinga/service.hpp" @@ -24,3 +25,5 @@ class IcingaCheckTask }; } + +#endif /* ICINGACHECKTASK_H */ diff --git a/lib/methods/ifwapichecktask.hpp b/lib/methods/ifwapichecktask.hpp index 8031e26924b..d105a09edac 100644 --- a/lib/methods/ifwapichecktask.hpp +++ b/lib/methods/ifwapichecktask.hpp @@ -1,7 +1,6 @@ -/* Icinga 2 | (c) 2021 Icinga GmbH | GPLv2+ */ +/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */ -#ifndef ICINGA4WINAPICHECKTASK_H -#define ICINGA4WINAPICHECKTASK_H +#pragma once #include "methods/i2-methods.hpp" #include "icinga/service.hpp" @@ -26,5 +25,3 @@ class IfwApiCheckTask }; } - -#endif /* ICINGA4WINAPICHECKTASK_H */ From 7f53d145803fd60c8b9947296aacf9e8152b3708 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 12:16:07 +0100 Subject: [PATCH 09/97] WIP --- lib/methods/ifwapichecktask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 86abed9e67d..85fe17e8776 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -50,10 +50,10 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes resolvers.emplace_back("command", command); resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); - String psCommand = MacroProcessor::ResolveMacros("$icinga4win_api_command_name$", resolvers, checkable->GetLastCheckResult(), + String psCommand = MacroProcessor::ResolveMacros("$ifw_api_command$", resolvers, checkable->GetLastCheckResult(), nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); - Dictionary::Ptr arguments = MacroProcessor::ResolveMacros("$icinga4win_api_arguments$", resolvers, checkable->GetLastCheckResult(), + Dictionary::Ptr arguments = MacroProcessor::ResolveMacros("$ifw_api_arguments$", resolvers, checkable->GetLastCheckResult(), nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); Dictionary::Ptr params = new Dictionary(); From 27ca87b8030a064e943bd5c0127ea26a5be1ee8f Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 12:31:41 +0100 Subject: [PATCH 10/97] WIP --- lib/methods/ifwapichecktask.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 85fe17e8776..b652b0c9893 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -60,17 +60,32 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (arguments) { ObjectLock oLock (arguments); - Array::Ptr dummy = new Array(); + Array::Ptr emptyCmd = new Array(); for (auto& kv : arguments) { + /* MacroProcessor::ResolveArguments() converts + * + * [ "check_example" ] + * and + * { + * "-f" = { set_if = "$example_flag$" } + * "-a" = "$example_arg$" + * } + * + * to + * + * [ "check_example", "-f", "-a", "X" ] + * + * but we need the args one-by-one like [ "-f" ] or [ "-a", "X" ]. + */ Array::Ptr arg = MacroProcessor::ResolveArguments( - dummy, new Dictionary({{kv.first, kv.second}}), resolvers, + emptyCmd, new Dictionary({{kv.first, kv.second}}), resolvers, checkable->GetLastCheckResult(), resolvedMacros, useResolvedMacros ); switch (arg ? arg->GetLength() : 0) { case 0: - continue; + break; case 1: params->Set(arg->Get(0), true); break; From f5c6600ff3d2c9ec677cf0ae5125307838d6a296 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 12:36:36 +0100 Subject: [PATCH 11/97] WIP --- itl/command-icinga.conf | 3 +++ lib/methods/ifwapichecktask.cpp | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index bac2e5feb50..56a71084769 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -42,4 +42,7 @@ object CheckCommand "sleep" { object CheckCommand "ifw-api" { import "ifw-api-check-command" + + vars.ifw_api_host = "localhost" + vars.ifw_api_port = 5668 } diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index b652b0c9893..56beb61b3ea 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -56,6 +56,12 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Dictionary::Ptr arguments = MacroProcessor::ResolveMacros("$ifw_api_arguments$", resolvers, checkable->GetLastCheckResult(), nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + String psHost = MacroProcessor::ResolveMacros("$ifw_api_host$", resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + double psPort = MacroProcessor::ResolveMacros("$ifw_api_port$", resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + Dictionary::Ptr params = new Dictionary(); if (arguments) { @@ -116,7 +122,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes req.set(field::content_type, "application/json"); req.body() = JsonEncode(params); - Connect(conn.lowest_layer(), "127.0.0.1", "5668"); + Connect(conn.lowest_layer(), psHost, psPort); conn.next_layer().handshake(conn.next_layer().client); double start = Utility::GetTime(); From 4c280320f9bad5939d23ebd8aa947ae3b268a331 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 13:24:42 +0100 Subject: [PATCH 12/97] WIP --- lib/methods/ifwapichecktask.cpp | 108 +++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 28 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 56beb61b3ea..bdd5c7169df 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -18,11 +18,39 @@ #include #include #include +#include using namespace icinga; REGISTER_FUNCTION_NONCONST(Internal, IfwApiCheck, &IfwApiCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +static void ReportIfwCheckResult( + const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, + const String& output, int exitcode = 3, const Array::Ptr& perfdata = nullptr +) +{ + if (Checkable::ExecuteCommandProcessFinishedHandler) { + ProcessResult pr; + pr.PID = -1; + pr.Output = perfdata ? output + " |" + perfdata->Join("") : output; + pr.ExecutionStart = start; + pr.ExecutionEnd = end; + pr.ExitStatus = exitcode; + + Checkable::ExecuteCommandProcessFinishedHandler(command->GetName(), pr); + } else { + cr->SetOutput(output); + cr->SetPerformanceData(perfdata); + cr->SetState((ServiceState)exitcode); + cr->SetExitStatus(exitcode); + cr->SetExecutionStart(start); + cr->SetExecutionEnd(end); + cr->SetCommand(command->GetName()); + + checkable->ProcessCheckResult(cr); + } +} + void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { @@ -59,7 +87,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes String psHost = MacroProcessor::ResolveMacros("$ifw_api_host$", resolvers, checkable->GetLastCheckResult(), nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); - double psPort = MacroProcessor::ResolveMacros("$ifw_api_port$", resolvers, checkable->GetLastCheckResult(), + String psPort = MacroProcessor::ResolveMacros("$ifw_api_port$", resolvers, checkable->GetLastCheckResult(), nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); Dictionary::Ptr params = new Dictionary(); @@ -122,37 +150,61 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes req.set(field::content_type, "application/json"); req.body() = JsonEncode(params); - Connect(conn.lowest_layer(), psHost, psPort); - conn.next_layer().handshake(conn.next_layer().client); - - double start = Utility::GetTime(); - - write(conn, req); - conn.flush(); - read(conn, buf, resp); + try { + Connect(conn.lowest_layer(), psHost, psPort); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + checkable, command, cr, + "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; + } - double end = Utility::GetTime(); + try { + conn.next_layer().handshake(conn.next_layer().client); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + checkable, command, cr, + "TLS handshake with IfW API on host '" + psHost + "' port '" + psPort + "' failed: " + ex.what() + ); + return; + } - Dictionary::Ptr result = Dictionary::Ptr(JsonDecode(resp.body()))->Get(psCommand); + double start = Utility::GetTime(); - if (Checkable::ExecuteCommandProcessFinishedHandler) { - ProcessResult pr; - pr.PID = -1; - pr.Output = result->Get("checkresult") + " |" + Array::Ptr(result->Get("perfdata"))->Join(""); - pr.ExecutionStart = start; - pr.ExecutionEnd = end; - pr.ExitStatus = result->Get("exitcode"); + try { + write(conn, req); + conn.flush(); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + checkable, command, cr, + "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; + } - Checkable::ExecuteCommandProcessFinishedHandler(command->GetName(), pr); - } else { - cr->SetOutput(result->Get("checkresult")); - cr->SetPerformanceData(result->Get("perfdata")); - cr->SetState((ServiceState)(int)result->Get("exitcode")); - cr->SetExitStatus(result->Get("exitcode")); - cr->SetExecutionStart(start); - cr->SetExecutionEnd(end); - cr->SetCommand(command->GetName()); + try { + read(conn, buf, resp); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + checkable, command, cr, + "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; + } - checkable->ProcessCheckResult(cr); + double end = Utility::GetTime(); + Dictionary::Ptr r; + + try { + r = Dictionary::Ptr(JsonDecode(resp.body()))->Get(psCommand); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + checkable, command, cr, + "Got bad JSON from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; } + + ReportIfwCheckResult(checkable, command, cr, r->Get("checkresult"), r->Get("exitcode"), r->Get("perfdata")); } From eac3ac97b3044f6ef6ea045a46fd7681766ac68e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 13:36:06 +0100 Subject: [PATCH 13/97] WIP --- lib/methods/ifwapichecktask.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index bdd5c7169df..693773ba82b 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -194,10 +194,10 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes } double end = Utility::GetTime(); - Dictionary::Ptr r; + Dictionary::Ptr result; try { - r = Dictionary::Ptr(JsonDecode(resp.body()))->Get(psCommand); + result = Dictionary::Ptr(JsonDecode(resp.body()))->Get(psCommand); } catch (const std::exception& ex) { ReportIfwCheckResult( checkable, command, cr, @@ -206,5 +206,17 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes return; } - ReportIfwCheckResult(checkable, command, cr, r->Get("checkresult"), r->Get("exitcode"), r->Get("perfdata")); + double exitcode; + + try { + exitcode = result->Get("exitcode"); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + checkable, command, cr, + "Got bad exitcode from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; + } + + ReportIfwCheckResult(checkable, command, cr, result->Get("checkresult"), exitcode, result->Get("perfdata")); } From ad9b581d43adcb6d58e6368ea5317616d335134f Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Feb 2023 13:38:50 +0100 Subject: [PATCH 14/97] WIP --- lib/methods/ifwapichecktask.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 693773ba82b..157c1d613a2 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -218,5 +218,17 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes return; } - ReportIfwCheckResult(checkable, command, cr, result->Get("checkresult"), exitcode, result->Get("perfdata")); + Array::Ptr perfdata; + + try { + perfdata = result->Get("perfdata"); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + checkable, command, cr, + "Got bad perfdata from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; + } + + ReportIfwCheckResult(checkable, command, cr, result->Get("checkresult"), exitcode, perfdata); } From 507c19f1557b56b855b33eda81830dddfa909188 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 20 Feb 2023 15:12:25 +0100 Subject: [PATCH 15/97] WIP --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 157c1d613a2..fb2ce080dac 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -32,7 +32,7 @@ static void ReportIfwCheckResult( if (Checkable::ExecuteCommandProcessFinishedHandler) { ProcessResult pr; pr.PID = -1; - pr.Output = perfdata ? output + " |" + perfdata->Join("") : output; + pr.Output = perfdata ? output + " |" + perfdata->Join(" ") : output; pr.ExecutionStart = start; pr.ExecutionEnd = end; pr.ExitStatus = exitcode; From 589e45c4b420775094e5cb51e57aa90e722592c4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 20 Feb 2023 16:44:13 +0100 Subject: [PATCH 16/97] WIP --- lib/methods/ifwapichecktask.cpp | 251 ++++++++++++++++++++------------ 1 file changed, 161 insertions(+), 90 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index fb2ce080dac..268a4b392a4 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -13,6 +13,7 @@ #include "base/io-engine.hpp" #include "base/json.hpp" #include "base/logger.hpp" +#include "base/shared.hpp" #include "base/tcpsocket.hpp" #include "base/tlsstream.hpp" #include @@ -51,6 +52,155 @@ static void ReportIfwCheckResult( } } +static void ReportIfwCheckResult( + boost::asio::yield_context yc, const Checkable::Ptr& checkable, + const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, const String& output +) +{ + CpuBoundWork cbw (yc); + + Utility::QueueAsyncCallback([checkable, command, cr, output]() { + ReportIfwCheckResult(checkable, command, cr, output); + }); +} + +static void ProcessIfwResponse( + const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, const String& psCommand, + const String& psHost, const String& psPort, const boost::beast::http::response& resp +) +{ + Value jsonRoot; + + try { + jsonRoot = JsonDecode(resp.body()); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + checkable, command, cr, + "Got bad JSON from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; + } + + if (!jsonRoot.IsObjectType()) { + ReportIfwCheckResult( + checkable, command, cr, + "Got JSON, but not an object, from IfW API on host '" + psHost + "' port '" + psPort + "'" + ); + return; + } + + Value jsonBranch; + + if (!Dictionary::Ptr(jsonRoot)->Get(psCommand, &jsonBranch)) { + ReportIfwCheckResult( + checkable, command, cr, + "Missing ." + psCommand + " in JSON object from IfW API on host '" + psHost + "' port '" + psPort + "'" + ); + return; + } + + if (!jsonBranch.IsObjectType()) { + ReportIfwCheckResult( + checkable, command, cr, + "." + psCommand + " is not an object in JSON from IfW API on host '" + psHost + "' port '" + psPort + "'" + ); + return; + } + + Dictionary::Ptr result = jsonBranch; + double exitcode; + + try { + exitcode = result->Get("exitcode"); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + checkable, command, cr, + "Got bad exitcode from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; + } + + Array::Ptr perfdata; + + try { + perfdata = result->Get("perfdata"); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + checkable, command, cr, + "Got bad perfdata from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; + } + + ReportIfwCheckResult(checkable, command, cr, result->Get("checkresult"), exitcode, perfdata); +} + +static void DoIfwNetIo( + boost::asio::yield_context yc, const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, + const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& psPort, + const boost::beast::http::request& req +) +{ + using namespace boost::asio; + using namespace boost::beast; + using namespace boost::beast::http; + + ssl::context ctx (ssl::context::tls); + AsioTlsStream conn (IoEngine::Get().GetIoContext(), ctx); + flat_buffer buf; + auto resp (Shared>::Make()) + + try { + Connect(conn.lowest_layer(), psHost, psPort, yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, command, cr, + "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; + } + + try { + conn.next_layer().async_handshake(conn.next_layer().client, yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, command, cr, + "TLS handshake with IfW API on host '" + psHost + "' port '" + psPort + "' failed: " + ex.what() + ); + return; + } + + double start = Utility::GetTime(); + + try { + async_write(conn, req, yc); + conn.async_flush(yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, command, cr, + "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; + } + + try { + async_read(conn, buf, *resp, yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, command, cr, + "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + ); + return; + } + + double end = Utility::GetTime(); + CpuBoundWork cbw (yc); + + Utility::QueueAsyncCallback([checkable, command, cr, psCommand, psHost, psPort, resp]() { + ProcessIfwResponse(checkable, command, cr, psCommand, psHost, psPort, *resp); + }); +} + void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { @@ -139,96 +289,17 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (resolvedMacros && !useResolvedMacros) return; - ssl::context ctx (ssl::context::tls); - AsioTlsStream conn (IoEngine::Get().GetIoContext(), ctx); - request req; - flat_buffer buf; - response resp; - - req.method(verb::post); - req.target("/v1/checker?command=" + psCommand); - req.set(field::content_type, "application/json"); - req.body() = JsonEncode(params); - - try { - Connect(conn.lowest_layer(), psHost, psPort); - } catch (const std::exception& ex) { - ReportIfwCheckResult( - checkable, command, cr, - "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() - ); - return; - } - - try { - conn.next_layer().handshake(conn.next_layer().client); - } catch (const std::exception& ex) { - ReportIfwCheckResult( - checkable, command, cr, - "TLS handshake with IfW API on host '" + psHost + "' port '" + psPort + "' failed: " + ex.what() - ); - return; - } + auto req (Shared>::Make()); - double start = Utility::GetTime(); + req->method(verb::post); + req->target("/v1/checker?command=" + psCommand); + req->set(field::content_type, "application/json"); + req->body() = JsonEncode(params); - try { - write(conn, req); - conn.flush(); - } catch (const std::exception& ex) { - ReportIfwCheckResult( - checkable, command, cr, - "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() - ); - return; - } - - try { - read(conn, buf, resp); - } catch (const std::exception& ex) { - ReportIfwCheckResult( - checkable, command, cr, - "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() - ); - return; - } - - double end = Utility::GetTime(); - Dictionary::Ptr result; - - try { - result = Dictionary::Ptr(JsonDecode(resp.body()))->Get(psCommand); - } catch (const std::exception& ex) { - ReportIfwCheckResult( - checkable, command, cr, - "Got bad JSON from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() - ); - return; - } - - double exitcode; - - try { - exitcode = result->Get("exitcode"); - } catch (const std::exception& ex) { - ReportIfwCheckResult( - checkable, command, cr, - "Got bad exitcode from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() - ); - return; - } - - Array::Ptr perfdata; - - try { - perfdata = result->Get("perfdata"); - } catch (const std::exception& ex) { - ReportIfwCheckResult( - checkable, command, cr, - "Got bad perfdata from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() - ); - return; - } - - ReportIfwCheckResult(checkable, command, cr, result->Get("checkresult"), exitcode, perfdata); + IoEngine::SpawnCoroutine( + IoEngine::Get().GetIoContext(), + [checkable, command, cr, psCommand, psHost, psPort, req](asio::yield_context yc) { + DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, psPort, *req); + } + ); } From 0a387fd877f8fd7a53e72000fd66fb9159ba92d3 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 20 Feb 2023 16:51:43 +0100 Subject: [PATCH 17/97] WIP --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 268a4b392a4..91705474b0d 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -33,7 +33,7 @@ static void ReportIfwCheckResult( if (Checkable::ExecuteCommandProcessFinishedHandler) { ProcessResult pr; pr.PID = -1; - pr.Output = perfdata ? output + " |" + perfdata->Join(" ") : output; + pr.Output = perfdata ? output + " |" + String(perfdata->Join(" ")) : output; pr.ExecutionStart = start; pr.ExecutionEnd = end; pr.ExitStatus = exitcode; From d652d7daaa0059deede06e34f8fb54a5d9f6c767 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 20 Feb 2023 17:03:51 +0100 Subject: [PATCH 18/97] WIP --- lib/methods/ifwapichecktask.cpp | 47 +++++++++++++++++---------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 91705474b0d..3915399df04 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -26,8 +26,8 @@ using namespace icinga; REGISTER_FUNCTION_NONCONST(Internal, IfwApiCheck, &IfwApiCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); static void ReportIfwCheckResult( - const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, - const String& output, int exitcode = 3, const Array::Ptr& perfdata = nullptr + const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, const String& output, + double start, double end, int exitcode = 3, const Array::Ptr& perfdata = nullptr ) { if (Checkable::ExecuteCommandProcessFinishedHandler) { @@ -53,20 +53,22 @@ static void ReportIfwCheckResult( } static void ReportIfwCheckResult( - boost::asio::yield_context yc, const Checkable::Ptr& checkable, - const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, const String& output + boost::asio::yield_context yc, const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, + const CheckResult::Ptr& cr, const String& output, double start ) { + double end = Utility::GetTime(); CpuBoundWork cbw (yc); - Utility::QueueAsyncCallback([checkable, command, cr, output]() { - ReportIfwCheckResult(checkable, command, cr, output); + Utility::QueueAsyncCallback([checkable, command, cr, output, start, end]() { + ReportIfwCheckResult(checkable, command, cr, output, start, end); }); } static void ProcessIfwResponse( - const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, const String& psCommand, - const String& psHost, const String& psPort, const boost::beast::http::response& resp + const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, + const String& psCommand, const String& psHost, const String& psPort, double start, double end, + const boost::beast::http::response& resp ) { Value jsonRoot; @@ -76,7 +78,7 @@ static void ProcessIfwResponse( } catch (const std::exception& ex) { ReportIfwCheckResult( checkable, command, cr, - "Got bad JSON from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + "Got bad JSON from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start, end ); return; } @@ -84,7 +86,7 @@ static void ProcessIfwResponse( if (!jsonRoot.IsObjectType()) { ReportIfwCheckResult( checkable, command, cr, - "Got JSON, but not an object, from IfW API on host '" + psHost + "' port '" + psPort + "'" + "Got JSON, but not an object, from IfW API on host '" + psHost + "' port '" + psPort + "'", start, end ); return; } @@ -94,7 +96,7 @@ static void ProcessIfwResponse( if (!Dictionary::Ptr(jsonRoot)->Get(psCommand, &jsonBranch)) { ReportIfwCheckResult( checkable, command, cr, - "Missing ." + psCommand + " in JSON object from IfW API on host '" + psHost + "' port '" + psPort + "'" + "Missing ." + psCommand + " in JSON object from IfW API on host '" + psHost + "' port '" + psPort + "'", start, end ); return; } @@ -102,7 +104,7 @@ static void ProcessIfwResponse( if (!jsonBranch.IsObjectType()) { ReportIfwCheckResult( checkable, command, cr, - "." + psCommand + " is not an object in JSON from IfW API on host '" + psHost + "' port '" + psPort + "'" + "." + psCommand + " is not an object in JSON from IfW API on host '" + psHost + "' port '" + psPort + "'", start, end ); return; } @@ -115,7 +117,7 @@ static void ProcessIfwResponse( } catch (const std::exception& ex) { ReportIfwCheckResult( checkable, command, cr, - "Got bad exitcode from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + "Got bad exitcode from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start, end ); return; } @@ -127,12 +129,12 @@ static void ProcessIfwResponse( } catch (const std::exception& ex) { ReportIfwCheckResult( checkable, command, cr, - "Got bad perfdata from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + "Got bad perfdata from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start, end ); return; } - ReportIfwCheckResult(checkable, command, cr, result->Get("checkresult"), exitcode, perfdata); + ReportIfwCheckResult(checkable, command, cr, result->Get("checkresult"), start, end, exitcode, perfdata); } static void DoIfwNetIo( @@ -149,13 +151,14 @@ static void DoIfwNetIo( AsioTlsStream conn (IoEngine::Get().GetIoContext(), ctx); flat_buffer buf; auto resp (Shared>::Make()) + double start = Utility::GetTime(); try { Connect(conn.lowest_layer(), psHost, psPort, yc); } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, command, cr, - "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start ); return; } @@ -165,20 +168,18 @@ static void DoIfwNetIo( } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, command, cr, - "TLS handshake with IfW API on host '" + psHost + "' port '" + psPort + "' failed: " + ex.what() + "TLS handshake with IfW API on host '" + psHost + "' port '" + psPort + "' failed: " + ex.what(), start ); return; } - double start = Utility::GetTime(); - try { async_write(conn, req, yc); conn.async_flush(yc); } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, command, cr, - "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start ); return; } @@ -188,7 +189,7 @@ static void DoIfwNetIo( } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, command, cr, - "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what() + "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start ); return; } @@ -196,8 +197,8 @@ static void DoIfwNetIo( double end = Utility::GetTime(); CpuBoundWork cbw (yc); - Utility::QueueAsyncCallback([checkable, command, cr, psCommand, psHost, psPort, resp]() { - ProcessIfwResponse(checkable, command, cr, psCommand, psHost, psPort, *resp); + Utility::QueueAsyncCallback([checkable, command, cr, psCommand, psHost, psPort, start, end, resp]() { + ProcessIfwResponse(checkable, command, cr, psCommand, psHost, psPort, start, end, *resp); }); } From 52f964f58e9c3ee0e298ac46a75cca1a36efd9c8 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 20 Feb 2023 17:05:16 +0100 Subject: [PATCH 19/97] WIP --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 3915399df04..9850c4c7b44 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -150,7 +150,7 @@ static void DoIfwNetIo( ssl::context ctx (ssl::context::tls); AsioTlsStream conn (IoEngine::Get().GetIoContext(), ctx); flat_buffer buf; - auto resp (Shared>::Make()) + auto resp (Shared>::Make()); double start = Utility::GetTime(); try { From f9143cee9a33c12f77a680a1a06d2d7c2c6e153f Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 20 Feb 2023 17:08:03 +0100 Subject: [PATCH 20/97] WIP --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 9850c4c7b44..d10e25b5f0a 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -299,7 +299,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes IoEngine::SpawnCoroutine( IoEngine::Get().GetIoContext(), - [checkable, command, cr, psCommand, psHost, psPort, req](asio::yield_context yc) { + [checkable, command, cr, psCommand, psHost, psPort, req](boost::asio::yield_context yc) { DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, psPort, *req); } ); From 95f3bbe023856c65b61b68754d2463659d2e9d69 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 20 Feb 2023 17:11:19 +0100 Subject: [PATCH 21/97] WIP --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index d10e25b5f0a..9c121188177 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -140,7 +140,7 @@ static void ProcessIfwResponse( static void DoIfwNetIo( boost::asio::yield_context yc, const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& psPort, - const boost::beast::http::request& req + boost::beast::http::request& req ) { using namespace boost::asio; From b88b900b67f9cbc59207ecac2e8dc85a13a58e01 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 20 Feb 2023 17:31:35 +0100 Subject: [PATCH 22/97] WIP --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 9c121188177..5892e8381b0 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -68,7 +68,7 @@ static void ReportIfwCheckResult( static void ProcessIfwResponse( const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& psPort, double start, double end, - const boost::beast::http::response& resp + boost::beast::http::response& resp ) { Value jsonRoot; From 073e28bc9fc4c8fa13e765761cc7140873bf30fb Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 20 Feb 2023 17:35:30 +0100 Subject: [PATCH 23/97] WIP --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 5892e8381b0..9c121188177 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -68,7 +68,7 @@ static void ReportIfwCheckResult( static void ProcessIfwResponse( const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& psPort, double start, double end, - boost::beast::http::response& resp + const boost::beast::http::response& resp ) { Value jsonRoot; From 4373defef4a535d334a9a1b08f2cf6cd1dda7bd4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 20 Feb 2023 17:43:41 +0100 Subject: [PATCH 24/97] WIP --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 9c121188177..4410b3f463b 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -185,7 +185,7 @@ static void DoIfwNetIo( } try { - async_read(conn, buf, *resp, yc); + async_read(conn, buf, *(response*)resp.get(), yc); } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, command, cr, From ac31369737a6cb49a151741667a9533814f565f4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 21 Feb 2023 12:57:49 +0100 Subject: [PATCH 25/97] WIP --- itl/command-icinga.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index 56a71084769..31c2e8b50fd 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -43,6 +43,8 @@ object CheckCommand "sleep" { object CheckCommand "ifw-api" { import "ifw-api-check-command" + vars.ifw_api_command = "$command.name$" + vars.ifw_api_arguments = "$command.arguments$" vars.ifw_api_host = "localhost" vars.ifw_api_port = 5668 } From 5eda4142a35aecfc3fa42d16c906ed6e57f91ed0 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 21 Feb 2023 13:17:22 +0100 Subject: [PATCH 26/97] WIP --- itl/command-icinga.conf | 1 + lib/methods/ifwapichecktask.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index 31c2e8b50fd..0b59c146867 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -45,6 +45,7 @@ object CheckCommand "ifw-api" { vars.ifw_api_command = "$command.name$" vars.ifw_api_arguments = "$command.arguments$" + vars.ifw_api_ignore_arguments = [ "-C", "-ExecutionPolicy", "-NoLogo", "-NoProfile" ] vars.ifw_api_host = "localhost" vars.ifw_api_port = 5668 } diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 4410b3f463b..64acc5e56ac 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -235,6 +235,9 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Dictionary::Ptr arguments = MacroProcessor::ResolveMacros("$ifw_api_arguments$", resolvers, checkable->GetLastCheckResult(), nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + Array::Ptr ignoreArguments = MacroProcessor::ResolveMacros("$ifw_api_ignore_arguments$", resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + String psHost = MacroProcessor::ResolveMacros("$ifw_api_host$", resolvers, checkable->GetLastCheckResult(), nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); @@ -244,10 +247,15 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Dictionary::Ptr params = new Dictionary(); if (arguments) { + auto ignore (ignoreArguments->ToSet()); ObjectLock oLock (arguments); Array::Ptr emptyCmd = new Array(); for (auto& kv : arguments) { + if (ignore.find(kv.first) != ignore.end()) { + continue; + } + /* MacroProcessor::ResolveArguments() converts * * [ "check_example" ] From a551131b4fb5c7992f681e15944395e17ee9d76e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 21 Feb 2023 16:49:04 +0100 Subject: [PATCH 27/97] WIP --- lib/methods/ifwapichecktask.cpp | 126 +++++++++++++++----------------- 1 file changed, 57 insertions(+), 69 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 64acc5e56ac..a2bc0240645 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -65,12 +65,65 @@ static void ReportIfwCheckResult( }); } -static void ProcessIfwResponse( - const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, - const String& psCommand, const String& psHost, const String& psPort, double start, double end, - const boost::beast::http::response& resp +static void DoIfwNetIo( + boost::asio::yield_context yc, const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, + const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& psPort, + boost::beast::http::request& req ) { + using namespace boost::asio; + using namespace boost::beast; + using namespace boost::beast::http; + + ssl::context ctx (ssl::context::tls); + AsioTlsStream conn (IoEngine::Get().GetIoContext(), ctx); + flat_buffer buf; + auto resp (Shared>::Make()); + double start = Utility::GetTime(); + + try { + Connect(conn.lowest_layer(), psHost, psPort, yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, command, cr, + "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start + ); + return; + } + + try { + conn.next_layer().async_handshake(conn.next_layer().client, yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, command, cr, + "TLS handshake with IfW API on host '" + psHost + "' port '" + psPort + "' failed: " + ex.what(), start + ); + return; + } + + try { + async_write(conn, req, yc); + conn.async_flush(yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, command, cr, + "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start + ); + return; + } + + try { + async_read(conn, buf, *(response*)resp.get(), yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, command, cr, + "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start + ); + return; + } + + double end = Utility::GetTime(); + CpuBoundWork cbw (yc); Value jsonRoot; try { @@ -137,71 +190,6 @@ static void ProcessIfwResponse( ReportIfwCheckResult(checkable, command, cr, result->Get("checkresult"), start, end, exitcode, perfdata); } -static void DoIfwNetIo( - boost::asio::yield_context yc, const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, - const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& psPort, - boost::beast::http::request& req -) -{ - using namespace boost::asio; - using namespace boost::beast; - using namespace boost::beast::http; - - ssl::context ctx (ssl::context::tls); - AsioTlsStream conn (IoEngine::Get().GetIoContext(), ctx); - flat_buffer buf; - auto resp (Shared>::Make()); - double start = Utility::GetTime(); - - try { - Connect(conn.lowest_layer(), psHost, psPort, yc); - } catch (const std::exception& ex) { - ReportIfwCheckResult( - yc, checkable, command, cr, - "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start - ); - return; - } - - try { - conn.next_layer().async_handshake(conn.next_layer().client, yc); - } catch (const std::exception& ex) { - ReportIfwCheckResult( - yc, checkable, command, cr, - "TLS handshake with IfW API on host '" + psHost + "' port '" + psPort + "' failed: " + ex.what(), start - ); - return; - } - - try { - async_write(conn, req, yc); - conn.async_flush(yc); - } catch (const std::exception& ex) { - ReportIfwCheckResult( - yc, checkable, command, cr, - "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start - ); - return; - } - - try { - async_read(conn, buf, *(response*)resp.get(), yc); - } catch (const std::exception& ex) { - ReportIfwCheckResult( - yc, checkable, command, cr, - "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start - ); - return; - } - - double end = Utility::GetTime(); - CpuBoundWork cbw (yc); - - Utility::QueueAsyncCallback([checkable, command, cr, psCommand, psHost, psPort, start, end, resp]() { - ProcessIfwResponse(checkable, command, cr, psCommand, psHost, psPort, start, end, *resp); - }); -} - void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { From 88a5ae8171091e9d05132e53b7bf03074aab1213 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 21 Feb 2023 16:50:02 +0100 Subject: [PATCH 28/97] WIP --- lib/methods/ifwapichecktask.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.hpp b/lib/methods/ifwapichecktask.hpp index d105a09edac..39327336b0b 100644 --- a/lib/methods/ifwapichecktask.hpp +++ b/lib/methods/ifwapichecktask.hpp @@ -10,7 +10,7 @@ namespace icinga { /** - * Executes checks via local Icinga for Windows API. + * Executes checks via Icinga for Windows API. * * @ingroup methods */ From fc48c9a8c1b8670e85c7822f62090b848ac49971 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 21 Feb 2023 16:52:21 +0100 Subject: [PATCH 29/97] WIP --- lib/methods/ifwapichecktask.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index a2bc0240645..766c8bc9131 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -267,13 +267,13 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes switch (arg ? arg->GetLength() : 0) { case 0: break; - case 1: + case 1: // [ "-f" ] params->Set(arg->Get(0), true); break; - case 2: + case 2: // [ "-a", "X" ] params->Set(arg->Get(0), arg->Get(1)); break; - default: { + default: { // [ "-a", "X", "Y" ] auto k (arg->Get(0)); arg->Remove(0); From 86d873a10aa25260c8de0190fe05f39b26375702 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 21 Feb 2023 17:26:31 +0100 Subject: [PATCH 30/97] WIP --- lib/methods/ifwapichecktask.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 766c8bc9131..3365519ae7f 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -244,6 +244,17 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes continue; } + Dictionary::Ptr argSpec; + + if (kv.second.IsObjectType()) { + argSpec = Dictionary::Ptr(kv.second)->ShallowClone(); + } else { + argSpec = new Dictionary({{ "value", kv.second }}); + } + + // See default branch of below switch + argSpec->Set("repeat_key", false); + /* MacroProcessor::ResolveArguments() converts * * [ "check_example" ] @@ -260,7 +271,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes * but we need the args one-by-one like [ "-f" ] or [ "-a", "X" ]. */ Array::Ptr arg = MacroProcessor::ResolveArguments( - emptyCmd, new Dictionary({{kv.first, kv.second}}), resolvers, + emptyCmd, new Dictionary({{kv.first, argSpec}}), resolvers, checkable->GetLastCheckResult(), resolvedMacros, useResolvedMacros ); From fd815dd234d477372aea5cdccadc3b696d4fc0de Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 21 Feb 2023 17:29:04 +0100 Subject: [PATCH 31/97] WIP --- lib/methods/ifwapichecktask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 3365519ae7f..d8b86c1299a 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -78,7 +78,7 @@ static void DoIfwNetIo( ssl::context ctx (ssl::context::tls); AsioTlsStream conn (IoEngine::Get().GetIoContext(), ctx); flat_buffer buf; - auto resp (Shared>::Make()); + response resp; double start = Utility::GetTime(); try { @@ -113,7 +113,7 @@ static void DoIfwNetIo( } try { - async_read(conn, buf, *(response*)resp.get(), yc); + async_read(conn, buf, resp, yc); } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, command, cr, From 65dd6b088df32e4dce95c0e92c1d48a3c4a5c7e8 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 21 Feb 2023 17:30:55 +0100 Subject: [PATCH 32/97] WIP --- lib/methods/ifwapichecktask.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index d8b86c1299a..65dbadef583 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -20,6 +20,7 @@ #include #include #include +#include using namespace icinga; @@ -297,17 +298,17 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (resolvedMacros && !useResolvedMacros) return; - auto req (Shared>::Make()); + request req; - req->method(verb::post); - req->target("/v1/checker?command=" + psCommand); - req->set(field::content_type, "application/json"); - req->body() = JsonEncode(params); + req.method(verb::post); + req.target("/v1/checker?command=" + psCommand); + req.set(field::content_type, "application/json"); + req.body() = JsonEncode(params); IoEngine::SpawnCoroutine( IoEngine::Get().GetIoContext(), - [checkable, command, cr, psCommand, psHost, psPort, req](boost::asio::yield_context yc) { - DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, psPort, *req); + [checkable, command, cr, psCommand, psHost, psPort, req=std::move(req)](boost::asio::yield_context yc) { + DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, psPort, req); } ); } From 091927fc73582b9d1d192bbe8546f509c845844d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 22 Feb 2023 12:26:40 +0100 Subject: [PATCH 33/97] Revert "WIP" This reverts commit 65dd6b088df32e4dce95c0e92c1d48a3c4a5c7e8. --- lib/methods/ifwapichecktask.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 65dbadef583..d8b86c1299a 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -20,7 +20,6 @@ #include #include #include -#include using namespace icinga; @@ -298,17 +297,17 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (resolvedMacros && !useResolvedMacros) return; - request req; + auto req (Shared>::Make()); - req.method(verb::post); - req.target("/v1/checker?command=" + psCommand); - req.set(field::content_type, "application/json"); - req.body() = JsonEncode(params); + req->method(verb::post); + req->target("/v1/checker?command=" + psCommand); + req->set(field::content_type, "application/json"); + req->body() = JsonEncode(params); IoEngine::SpawnCoroutine( IoEngine::Get().GetIoContext(), - [checkable, command, cr, psCommand, psHost, psPort, req=std::move(req)](boost::asio::yield_context yc) { - DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, psPort, req); + [checkable, command, cr, psCommand, psHost, psPort, req](boost::asio::yield_context yc) { + DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, psPort, *req); } ); } From d3bb94fc39e2583806976aa6ff66ef150ae6b801 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 22 Feb 2023 12:41:04 +0100 Subject: [PATCH 34/97] Basic auth --- lib/methods/ifwapichecktask.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index d8b86c1299a..7ff31a835b1 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -6,6 +6,7 @@ #include "methods/ifwapichecktask.hpp" #include "icinga/icingaapplication.hpp" #include "icinga/pluginutility.hpp" +#include "base/base64.hpp" #include "base/utility.hpp" #include "base/perfdatavalue.hpp" #include "base/convert.hpp" @@ -232,6 +233,14 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes String psPort = MacroProcessor::ResolveMacros("$ifw_api_port$", resolvers, checkable->GetLastCheckResult(), nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + String missingUsername, missingPassword; + + String username = MacroProcessor::ResolveMacros("$ifw_api_username$", resolvers, checkable->GetLastCheckResult(), + &missingUsername, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + String password = MacroProcessor::ResolveMacros("$ifw_api_password$", resolvers, checkable->GetLastCheckResult(), + &missingPassword, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + Dictionary::Ptr params = new Dictionary(); if (arguments) { @@ -304,6 +313,10 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes req->set(field::content_type, "application/json"); req->body() = JsonEncode(params); + if (missingUsername.IsEmpty() && missingPassword.IsEmpty()) { + req->set(field::authorization, "Basic " + Base64::Encode(username + ":" + password)); + } + IoEngine::SpawnCoroutine( IoEngine::Get().GetIoContext(), [checkable, command, cr, psCommand, psHost, psPort, req](boost::asio::yield_context yc) { From 45cb95d0c41b72e4816cd49fb44fe9abfd293051 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 4 Apr 2023 11:09:01 +0200 Subject: [PATCH 35/97] WIP --- lib/methods/ifwapichecktask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 7ff31a835b1..ba458f3158e 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -216,7 +216,6 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes resolvers.emplace_back("service", service); resolvers.emplace_back("host", host); resolvers.emplace_back("command", command); - resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); String psCommand = MacroProcessor::ResolveMacros("$ifw_api_command$", resolvers, checkable->GetLastCheckResult(), nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); From efc01c004899b2d964bd220088d9441dc1ead714 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 4 Apr 2023 11:17:00 +0200 Subject: [PATCH 36/97] WIP --- lib/methods/ifwapichecktask.cpp | 35 +++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index ba458f3158e..57103b87ead 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -202,6 +202,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes REQUIRE_NOT_NULL(cr); CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); + auto cr (checkable->GetLastCheckResult()); Host::Ptr host; Service::Ptr service; @@ -217,28 +218,21 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes resolvers.emplace_back("host", host); resolvers.emplace_back("command", command); - String psCommand = MacroProcessor::ResolveMacros("$ifw_api_command$", resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); - - Dictionary::Ptr arguments = MacroProcessor::ResolveMacros("$ifw_api_arguments$", resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); - - Array::Ptr ignoreArguments = MacroProcessor::ResolveMacros("$ifw_api_ignore_arguments$", resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); - - String psHost = MacroProcessor::ResolveMacros("$ifw_api_host$", resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); - - String psPort = MacroProcessor::ResolveMacros("$ifw_api_port$", resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + auto resolveMacros ([&resolvers, &cr, &resolvedMacros, useResolvedMacros](const char* macros, String* missingMacro = nullptr) -> Value { + return MacroProcessor::ResolveMacros( + macros, resolvers, cr, missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros + ); + }); String missingUsername, missingPassword; - String username = MacroProcessor::ResolveMacros("$ifw_api_username$", resolvers, checkable->GetLastCheckResult(), - &missingUsername, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); - - String password = MacroProcessor::ResolveMacros("$ifw_api_password$", resolvers, checkable->GetLastCheckResult(), - &missingPassword, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + String psCommand = resolveMacros("$ifw_api_command$"); + Dictionary::Ptr arguments = resolveMacros("$ifw_api_arguments$"); + Array::Ptr ignoreArguments = resolveMacros("$ifw_api_ignore_arguments$"); + String psHost = resolveMacros("$ifw_api_host$"); + String psPort = resolveMacros("$ifw_api_port$"); + String username = resolveMacros("$ifw_api_username$", &missingUsername); + String password = resolveMacros("$ifw_api_password$", &missingPassword); Dictionary::Ptr params = new Dictionary(); @@ -279,8 +273,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes * but we need the args one-by-one like [ "-f" ] or [ "-a", "X" ]. */ Array::Ptr arg = MacroProcessor::ResolveArguments( - emptyCmd, new Dictionary({{kv.first, argSpec}}), resolvers, - checkable->GetLastCheckResult(), resolvedMacros, useResolvedMacros + emptyCmd, new Dictionary({{kv.first, argSpec}}), resolvers, cr, resolvedMacros, useResolvedMacros ); switch (arg ? arg->GetLength() : 0) { From 989eabd389864a14ed5b0a14c751365c71e5996d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 4 Apr 2023 11:38:45 +0200 Subject: [PATCH 37/97] WIP --- lib/methods/ifwapichecktask.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 57103b87ead..cac7c414bdb 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -202,7 +202,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes REQUIRE_NOT_NULL(cr); CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); - auto cr (checkable->GetLastCheckResult()); + auto lcr (checkable->GetLastCheckResult()); Host::Ptr host; Service::Ptr service; @@ -218,9 +218,9 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes resolvers.emplace_back("host", host); resolvers.emplace_back("command", command); - auto resolveMacros ([&resolvers, &cr, &resolvedMacros, useResolvedMacros](const char* macros, String* missingMacro = nullptr) -> Value { + auto resolveMacros ([&resolvers, &lcr, &resolvedMacros, useResolvedMacros](const char* macros, String* missingMacro = nullptr) -> Value { return MacroProcessor::ResolveMacros( - macros, resolvers, cr, missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros + macros, resolvers, lcr, missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros ); }); @@ -273,7 +273,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes * but we need the args one-by-one like [ "-f" ] or [ "-a", "X" ]. */ Array::Ptr arg = MacroProcessor::ResolveArguments( - emptyCmd, new Dictionary({{kv.first, argSpec}}), resolvers, cr, resolvedMacros, useResolvedMacros + emptyCmd, new Dictionary({{kv.first, argSpec}}), resolvers, lcr, resolvedMacros, useResolvedMacros ); switch (arg ? arg->GetLength() : 0) { From 5a4098e930c9df6f6d98373c8f484fb4d9804bb6 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 4 Apr 2023 11:44:35 +0200 Subject: [PATCH 38/97] WIP --- itl/command-icinga.conf | 5 +++++ lib/methods/ifwapichecktask.cpp | 23 ++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index 0b59c146867..c9797ce45c7 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -43,9 +43,14 @@ object CheckCommand "sleep" { object CheckCommand "ifw-api" { import "ifw-api-check-command" + var certsDir = Configuration.DataDir + "/certs/" + vars.ifw_api_command = "$command.name$" vars.ifw_api_arguments = "$command.arguments$" vars.ifw_api_ignore_arguments = [ "-C", "-ExecutionPolicy", "-NoLogo", "-NoProfile" ] vars.ifw_api_host = "localhost" vars.ifw_api_port = 5668 + vars.ifw_api_cert = certsDir + NodeName + ".crt" + vars.ifw_api_key = certsDir + NodeName + ".key" + vars.ifw_api_ca = certsDir + "ca.crt" } diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index cac7c414bdb..ede1aa8b0f4 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -69,14 +69,13 @@ static void ReportIfwCheckResult( static void DoIfwNetIo( boost::asio::yield_context yc, const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& psPort, - boost::beast::http::request& req + boost::asio::ssl::context& ctx, boost::beast::http::request& req ) { using namespace boost::asio; using namespace boost::beast; using namespace boost::beast::http; - ssl::context ctx (ssl::context::tls); AsioTlsStream conn (IoEngine::Get().GetIoContext(), ctx); flat_buffer buf; response resp; @@ -224,13 +223,17 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes ); }); - String missingUsername, missingPassword; + String missingCrl, missingUsername, missingPassword; String psCommand = resolveMacros("$ifw_api_command$"); Dictionary::Ptr arguments = resolveMacros("$ifw_api_arguments$"); Array::Ptr ignoreArguments = resolveMacros("$ifw_api_ignore_arguments$"); String psHost = resolveMacros("$ifw_api_host$"); String psPort = resolveMacros("$ifw_api_port$"); + String cert = resolveMacros("$ifw_api_cert$"); + String key = resolveMacros("$ifw_api_key$"); + String ca = resolveMacros("$ifw_api_ca$"); + String crl = resolveMacros("$ifw_api_crl$", &missingCrl); String username = resolveMacros("$ifw_api_username$", &missingUsername); String password = resolveMacros("$ifw_api_password$", &missingPassword); @@ -298,6 +301,16 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (resolvedMacros && !useResolvedMacros) return; + Shared::Ptr ctx; + + try { + ctx = SetupSslContext(cert, key, ca, crl, DEFAULT_TLS_CIPHERS, DEFAULT_TLS_PROTOCOLMIN, DebugInfo()); + } catch (const std::exception& ex) { + double now = Utility::GetTime(); + ReportIfwCheckResult(checkable, command, cr, ex.what(), now, now); + return; + } + auto req (Shared>::Make()); req->method(verb::post); @@ -311,8 +324,8 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes IoEngine::SpawnCoroutine( IoEngine::Get().GetIoContext(), - [checkable, command, cr, psCommand, psHost, psPort, req](boost::asio::yield_context yc) { - DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, psPort, *req); + [checkable, command, cr, psCommand, psHost, psPort, ctx, req](boost::asio::yield_context yc) { + DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, psPort, *ctx, *req); } ); } From 7a920c5de343a63be9d7da0db41573717d775f55 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 4 Apr 2023 11:51:11 +0200 Subject: [PATCH 39/97] WIP --- itl/command-icinga.conf | 5 ----- lib/methods/ifwapichecktask.cpp | 21 +++++++++++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index c9797ce45c7..0b59c146867 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -43,14 +43,9 @@ object CheckCommand "sleep" { object CheckCommand "ifw-api" { import "ifw-api-check-command" - var certsDir = Configuration.DataDir + "/certs/" - vars.ifw_api_command = "$command.name$" vars.ifw_api_arguments = "$command.arguments$" vars.ifw_api_ignore_arguments = [ "-C", "-ExecutionPolicy", "-NoLogo", "-NoProfile" ] vars.ifw_api_host = "localhost" vars.ifw_api_port = 5668 - vars.ifw_api_cert = certsDir + NodeName + ".crt" - vars.ifw_api_key = certsDir + NodeName + ".key" - vars.ifw_api_ca = certsDir + "ca.crt" } diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index ede1aa8b0f4..4bd5173f52e 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -17,6 +17,7 @@ #include "base/shared.hpp" #include "base/tcpsocket.hpp" #include "base/tlsstream.hpp" +#include "remote/apilistener.hpp" #include #include #include @@ -223,16 +224,16 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes ); }); - String missingCrl, missingUsername, missingPassword; + String missingCert, missingKey, missingCa, missingCrl, missingUsername, missingPassword; String psCommand = resolveMacros("$ifw_api_command$"); Dictionary::Ptr arguments = resolveMacros("$ifw_api_arguments$"); Array::Ptr ignoreArguments = resolveMacros("$ifw_api_ignore_arguments$"); String psHost = resolveMacros("$ifw_api_host$"); String psPort = resolveMacros("$ifw_api_port$"); - String cert = resolveMacros("$ifw_api_cert$"); - String key = resolveMacros("$ifw_api_key$"); - String ca = resolveMacros("$ifw_api_ca$"); + String cert = resolveMacros("$ifw_api_cert$", missingCert); + String key = resolveMacros("$ifw_api_key$", missingKey); + String ca = resolveMacros("$ifw_api_ca$", missingCa); String crl = resolveMacros("$ifw_api_crl$", &missingCrl); String username = resolveMacros("$ifw_api_username$", &missingUsername); String password = resolveMacros("$ifw_api_password$", &missingPassword); @@ -301,6 +302,18 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (resolvedMacros && !useResolvedMacros) return; + if (!missingCert.IsEmpty()) { + cert = ApiListener::GetDefaultCertPath(); + } + + if (!missingKey.IsEmpty()) { + key = ApiListener::GetDefaultKeyPath(); + } + + if (!missingCa.IsEmpty()) { + ca = ApiListener::GetDefaultCaPath(); + } + Shared::Ptr ctx; try { From 7d25a89569a19ec07cc5c968c245bc18487d1cc8 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 4 Apr 2023 12:08:38 +0200 Subject: [PATCH 40/97] WIP --- lib/methods/ifwapichecktask.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 4bd5173f52e..1e9523a29f9 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -231,9 +231,9 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Array::Ptr ignoreArguments = resolveMacros("$ifw_api_ignore_arguments$"); String psHost = resolveMacros("$ifw_api_host$"); String psPort = resolveMacros("$ifw_api_port$"); - String cert = resolveMacros("$ifw_api_cert$", missingCert); - String key = resolveMacros("$ifw_api_key$", missingKey); - String ca = resolveMacros("$ifw_api_ca$", missingCa); + String cert = resolveMacros("$ifw_api_cert$", &missingCert); + String key = resolveMacros("$ifw_api_key$", &missingKey); + String ca = resolveMacros("$ifw_api_ca$", &missingCa); String crl = resolveMacros("$ifw_api_crl$", &missingCrl); String username = resolveMacros("$ifw_api_username$", &missingUsername); String password = resolveMacros("$ifw_api_password$", &missingPassword); From 07f6f6e3c5a8696a83bb0c55957740d57423e23e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 4 Apr 2023 14:45:19 +0200 Subject: [PATCH 41/97] WIP: Escape URL --- lib/methods/ifwapichecktask.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 1e9523a29f9..e13772c024f 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -18,6 +18,7 @@ #include "base/tcpsocket.hpp" #include "base/tlsstream.hpp" #include "remote/apilistener.hpp" +#include "remote/url.hpp" #include #include #include @@ -324,10 +325,15 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes return; } + Url::Ptr uri = new Url(); + + uri->SetPath({ "v1", "checker" }); + uri->SetQuery({{ "command", psCommand }}); + auto req (Shared>::Make()); req->method(verb::post); - req->target("/v1/checker?command=" + psCommand); + req->target(uri->Format()); req->set(field::content_type, "application/json"); req->body() = JsonEncode(params); From 4fda32bd9e444924d72529c706e31a3d6a6eab5b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 4 Apr 2023 15:23:41 +0200 Subject: [PATCH 42/97] WIP --- lib/methods/ifwapichecktask.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index e13772c024f..364bf186f61 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -125,6 +125,12 @@ static void DoIfwNetIo( } double end = Utility::GetTime(); + + { + boost::system::error_code ec; + conn.next_layer().async_shutdown(ec); + } + CpuBoundWork cbw (yc); Value jsonRoot; From 44a52b27006c168d6fa7c4909d67e4944b2216fe Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 4 Apr 2023 15:24:35 +0200 Subject: [PATCH 43/97] WIP --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 364bf186f61..31f2de5d2f9 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -128,7 +128,7 @@ static void DoIfwNetIo( { boost::system::error_code ec; - conn.next_layer().async_shutdown(ec); + conn.next_layer().async_shutdown(yc[ec]); } CpuBoundWork cbw (yc); From 5a1ca5fb66bb2055ea17048f7887194289043545 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 4 Apr 2023 15:59:02 +0200 Subject: [PATCH 44/97] WIP timeout --- lib/methods/ifwapichecktask.cpp | 53 +++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 31f2de5d2f9..2c07d7f7f50 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -71,17 +71,15 @@ static void ReportIfwCheckResult( static void DoIfwNetIo( boost::asio::yield_context yc, const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& psPort, - boost::asio::ssl::context& ctx, boost::beast::http::request& req + AsioTlsStream& conn, boost::beast::http::request& req, double start ) { using namespace boost::asio; using namespace boost::beast; using namespace boost::beast::http; - AsioTlsStream conn (IoEngine::Get().GetIoContext(), ctx); flat_buffer buf; response resp; - double start = Utility::GetTime(); try { Connect(conn.lowest_layer(), psHost, psPort, yc); @@ -306,6 +304,12 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes } } + auto timeout (command->GetTimeout()); + auto checkTimeout (checkable->GetCheckTimeout()); + + if (!checkTimeout.IsEmpty()) + timeout = checkTimeout; + if (resolvedMacros && !useResolvedMacros) return; @@ -321,16 +325,6 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes ca = ApiListener::GetDefaultCaPath(); } - Shared::Ptr ctx; - - try { - ctx = SetupSslContext(cert, key, ca, crl, DEFAULT_TLS_CIPHERS, DEFAULT_TLS_PROTOCOLMIN, DebugInfo()); - } catch (const std::exception& ex) { - double now = Utility::GetTime(); - ReportIfwCheckResult(checkable, command, cr, ex.what(), now, now); - return; - } - Url::Ptr uri = new Url(); uri->SetPath({ "v1", "checker" }); @@ -347,10 +341,37 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes req->set(field::authorization, "Basic " + Base64::Encode(username + ":" + password)); } + auto& io (IoEngine::Get().GetIoContext()); + auto strand (Shared::Make(io)); + Shared::Ptr ctx; + double start = Utility::GetTime(); + + try { + ctx = SetupSslContext(cert, key, ca, crl, DEFAULT_TLS_CIPHERS, DEFAULT_TLS_PROTOCOLMIN, DebugInfo()); + } catch (const std::exception& ex) { + ReportIfwCheckResult(checkable, command, cr, ex.what(), start, Utility::GetTime()); + return; + } + + auto conn (Shared::Make(io, *ctx)); + IoEngine::SpawnCoroutine( - IoEngine::Get().GetIoContext(), - [checkable, command, cr, psCommand, psHost, psPort, ctx, req](boost::asio::yield_context yc) { - DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, psPort, *ctx, *req); + *strand, + [strand, checkable, command, cr, psCommand, psHost, psPort, conn, req, start, timeout](boost::asio::yield_context yc) { + Timeout::Ptr timeout = new Timeout(strand->context(), *strand, boost::posix_time::microseconds(int64_t(timeout * 1e6)), + [&conn, &checkable](boost::asio::yield_context yc) { + Log(LogNotice, "IfwApiCheckTask") + << "Timeout while checking " << checkable->GetReflectionType()->GetName() + << " '" << checkable->GetName() << "', cancelling attempt"; + + boost::system::error_code ec; + conn->lowest_layer().cancel(ec); + } + ); + + Defer cancelTimeout ([&timeout]() { timeout->Cancel(); }); + + DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, psPort, *conn, *req, start); } ); } From 6867b82a1a23a55b73341847b0190978b74467b6 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 4 Apr 2023 16:06:06 +0200 Subject: [PATCH 45/97] WIP --- lib/methods/ifwapichecktask.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 2c07d7f7f50..d609094b21b 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -7,6 +7,7 @@ #include "icinga/icingaapplication.hpp" #include "icinga/pluginutility.hpp" #include "base/base64.hpp" +#include "base/defer.hpp" #include "base/utility.hpp" #include "base/perfdatavalue.hpp" #include "base/convert.hpp" @@ -304,11 +305,11 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes } } - auto timeout (command->GetTimeout()); - auto checkTimeout (checkable->GetCheckTimeout()); + auto checkTimeout (command->GetTimeout()); + auto checkableTimeout (checkable->GetCheckTimeout()); - if (!checkTimeout.IsEmpty()) - timeout = checkTimeout; + if (!checkableTimeout.IsEmpty()) + checkTimeout = checkableTimeout; if (resolvedMacros && !useResolvedMacros) return; @@ -342,7 +343,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes } auto& io (IoEngine::Get().GetIoContext()); - auto strand (Shared::Make(io)); + auto strand (Shared::Make(io)); Shared::Ptr ctx; double start = Utility::GetTime(); @@ -357,8 +358,8 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes IoEngine::SpawnCoroutine( *strand, - [strand, checkable, command, cr, psCommand, psHost, psPort, conn, req, start, timeout](boost::asio::yield_context yc) { - Timeout::Ptr timeout = new Timeout(strand->context(), *strand, boost::posix_time::microseconds(int64_t(timeout * 1e6)), + [strand, checkable, command, cr, psCommand, psHost, psPort, conn, req, start, checkTimeout](boost::asio::yield_context yc) { + Timeout::Ptr timeout = new Timeout(strand->context(), *strand, boost::posix_time::microseconds(int64_t(checkTimeout * 1e6)), [&conn, &checkable](boost::asio::yield_context yc) { Log(LogNotice, "IfwApiCheckTask") << "Timeout while checking " << checkable->GetReflectionType()->GetName() From 36d9b37bf59db10e76adaacd4d1ee48b354caa7c Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 5 Apr 2023 10:59:00 +0200 Subject: [PATCH 46/97] WIP SNI --- itl/command-icinga.conf | 1 + lib/methods/ifwapichecktask.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index 0b59c146867..b8440c6b243 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -48,4 +48,5 @@ object CheckCommand "ifw-api" { vars.ifw_api_ignore_arguments = [ "-C", "-ExecutionPolicy", "-NoLogo", "-NoProfile" ] vars.ifw_api_host = "localhost" vars.ifw_api_port = 5668 + vars.ifw_api_sni = NodeName } diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index d609094b21b..be6e31081d1 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -237,6 +237,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Array::Ptr ignoreArguments = resolveMacros("$ifw_api_ignore_arguments$"); String psHost = resolveMacros("$ifw_api_host$"); String psPort = resolveMacros("$ifw_api_port$"); + String sni = resolveMacros("$ifw_api_sni$"); String cert = resolveMacros("$ifw_api_cert$", &missingCert); String key = resolveMacros("$ifw_api_key$", &missingKey); String ca = resolveMacros("$ifw_api_ca$", &missingCa); @@ -354,7 +355,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes return; } - auto conn (Shared::Make(io, *ctx)); + auto conn (Shared::Make(io, *ctx, sni)); IoEngine::SpawnCoroutine( *strand, From 977a53f3729f54bd57cdbb3751850256ae026fdb Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 5 Apr 2023 11:04:45 +0200 Subject: [PATCH 47/97] WIP --- lib/methods/ifwapichecktask.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index be6e31081d1..415a50902fb 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -92,8 +92,10 @@ static void DoIfwNetIo( return; } + auto& sslConn (conn.next_layer()); + try { - conn.next_layer().async_handshake(conn.next_layer().client, yc); + sslConn.async_handshake(conn.next_layer().client, yc); } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, command, cr, @@ -127,7 +129,7 @@ static void DoIfwNetIo( { boost::system::error_code ec; - conn.next_layer().async_shutdown(yc[ec]); + sslConn.async_shutdown(yc[ec]); } CpuBoundWork cbw (yc); From 0649504f00d949f97a87af92a26eaa86189d681e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 5 Apr 2023 11:15:42 +0200 Subject: [PATCH 48/97] WIP Certificate validation --- lib/methods/ifwapichecktask.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 415a50902fb..cd8178f413f 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -104,6 +104,16 @@ static void DoIfwNetIo( return; } + if (!sslConn.IsVerifyOK()) { + ReportIfwCheckResult( + yc, checkable, command, cr, + "Certificate validation failed for IfW API on host '" + psHost + + "' port '" + psPort + "': " + sslConn.GetVerifyError(), + start + ); + return; + } + try { async_write(conn, req, yc); conn.async_flush(yc); From 9314c8c8a12e2f26ca631098dfd1f8c178e3fc5e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 5 Apr 2023 11:51:36 +0200 Subject: [PATCH 49/97] WIP --- itl/command-icinga.conf | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index b8440c6b243..cc554a19a45 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -48,5 +48,9 @@ object CheckCommand "ifw-api" { vars.ifw_api_ignore_arguments = [ "-C", "-ExecutionPolicy", "-NoLogo", "-NoProfile" ] vars.ifw_api_host = "localhost" vars.ifw_api_port = 5668 - vars.ifw_api_sni = NodeName + + vars.ifw_api_sni = function() { + var ifw_api_host = macro("$ifw_api_host$") + return ifw_api_host == "localhost" ? NodeName : ifw_api_host + } } From e94f54cd54347303be6ab8b732a45d8252d23334 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 11 Apr 2023 11:12:02 +0200 Subject: [PATCH 50/97] TLS err - print SNI --- lib/methods/ifwapichecktask.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index cd8178f413f..07d565de7ec 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -71,7 +71,7 @@ static void ReportIfwCheckResult( static void DoIfwNetIo( boost::asio::yield_context yc, const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, - const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& psPort, + const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& sni, const String& psPort, AsioTlsStream& conn, boost::beast::http::request& req, double start ) { @@ -99,7 +99,8 @@ static void DoIfwNetIo( } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, command, cr, - "TLS handshake with IfW API on host '" + psHost + "' port '" + psPort + "' failed: " + ex.what(), start + "TLS handshake with IfW API on host '" + psHost + "' (SNI '" + sni + + "') port '" + psPort + "' failed: " + ex.what(), start ); return; } @@ -107,8 +108,8 @@ static void DoIfwNetIo( if (!sslConn.IsVerifyOK()) { ReportIfwCheckResult( yc, checkable, command, cr, - "Certificate validation failed for IfW API on host '" + psHost - + "' port '" + psPort + "': " + sslConn.GetVerifyError(), + "Certificate validation failed for IfW API on host '" + psHost + "' (SNI '" + + sni + "') port '" + psPort + "': " + sslConn.GetVerifyError(), start ); return; @@ -371,7 +372,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes IoEngine::SpawnCoroutine( *strand, - [strand, checkable, command, cr, psCommand, psHost, psPort, conn, req, start, checkTimeout](boost::asio::yield_context yc) { + [strand, checkable, command, cr, psCommand, psHost, sni, psPort, conn, req, start, checkTimeout](boost::asio::yield_context yc) { Timeout::Ptr timeout = new Timeout(strand->context(), *strand, boost::posix_time::microseconds(int64_t(checkTimeout * 1e6)), [&conn, &checkable](boost::asio::yield_context yc) { Log(LogNotice, "IfwApiCheckTask") @@ -385,7 +386,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Defer cancelTimeout ([&timeout]() { timeout->Cancel(); }); - DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, psPort, *conn, *req, start); + DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, sni, psPort, *conn, *req, start); } ); } From df84af17f8285f53ed352fc18729aef674e40035 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 11 Apr 2023 11:58:23 +0200 Subject: [PATCH 51/97] TLS err - show CN --- lib/methods/ifwapichecktask.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 07d565de7ec..f255e746b78 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -99,17 +99,25 @@ static void DoIfwNetIo( } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, command, cr, - "TLS handshake with IfW API on host '" + psHost + "' (SNI '" + sni + "TLS handshake with IfW API on host '" + psHost + "' (SNI: '" + sni + "') port '" + psPort + "' failed: " + ex.what(), start ); return; } if (!sslConn.IsVerifyOK()) { + auto cert (sslConn.GetPeerCertificate()); + Value cn; + + try { + cn = GetCertificateCN(cert); + } catch (const std::exception&) { + } + ReportIfwCheckResult( yc, checkable, command, cr, - "Certificate validation failed for IfW API on host '" + psHost + "' (SNI '" - + sni + "') port '" + psPort + "': " + sslConn.GetVerifyError(), + "Certificate validation failed for IfW API on host '" + psHost + "' (SNI: '" + sni + "'; CN: " + + (cn.IsString() ? "'" + cn + "'" : "N/A") + ") port '" + psPort + "': " + sslConn.GetVerifyError(), start ); return; From b78ff583880b4ecdc80680cd869fcf78d22c0959 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 11 Apr 2023 12:49:21 +0200 Subject: [PATCH 52/97] WIP --- itl/command-icinga.conf | 7 ++----- lib/methods/ifwapichecktask.cpp | 5 +++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index cc554a19a45..c650838c05b 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -48,9 +48,6 @@ object CheckCommand "ifw-api" { vars.ifw_api_ignore_arguments = [ "-C", "-ExecutionPolicy", "-NoLogo", "-NoProfile" ] vars.ifw_api_host = "localhost" vars.ifw_api_port = 5668 - - vars.ifw_api_sni = function() { - var ifw_api_host = macro("$ifw_api_host$") - return ifw_api_host == "localhost" ? NodeName : ifw_api_host - } + vars.ifw_api_sni = "$ifw_api_host$" + vars.ifw_api_sni_denylist = [ "localhost" ] } diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index f255e746b78..63754abde9b 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -259,6 +259,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes String psHost = resolveMacros("$ifw_api_host$"); String psPort = resolveMacros("$ifw_api_port$"); String sni = resolveMacros("$ifw_api_sni$"); + Array::Ptr sniDenylist = resolveMacros("$ifw_api_sni_denylist$"); String cert = resolveMacros("$ifw_api_cert$", &missingCert); String key = resolveMacros("$ifw_api_key$", &missingKey); String ca = resolveMacros("$ifw_api_ca$", &missingCa); @@ -333,6 +334,10 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (!checkableTimeout.IsEmpty()) checkTimeout = checkableTimeout; + if (sniDenylist->Contains(sni)) { + sni = IcingaApplication::GetInstance()->GetNodeName(); + } + if (resolvedMacros && !useResolvedMacros) return; From 3d51610640b45e76cb8609a9536ced17be72a3ed Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 11 Apr 2023 17:14:15 +0200 Subject: [PATCH 53/97] ifw_api_expected_san, not ifw_api_sni --- itl/command-icinga.conf | 2 +- lib/methods/ifwapichecktask.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index c650838c05b..f515ad89011 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -48,6 +48,6 @@ object CheckCommand "ifw-api" { vars.ifw_api_ignore_arguments = [ "-C", "-ExecutionPolicy", "-NoLogo", "-NoProfile" ] vars.ifw_api_host = "localhost" vars.ifw_api_port = 5668 - vars.ifw_api_sni = "$ifw_api_host$" + vars.ifw_api_expected_san = "$ifw_api_host$" vars.ifw_api_sni_denylist = [ "localhost" ] } diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 63754abde9b..de352a06691 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -71,7 +71,7 @@ static void ReportIfwCheckResult( static void DoIfwNetIo( boost::asio::yield_context yc, const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, - const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& sni, const String& psPort, + const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& san, const String& psPort, AsioTlsStream& conn, boost::beast::http::request& req, double start ) { @@ -99,7 +99,7 @@ static void DoIfwNetIo( } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, command, cr, - "TLS handshake with IfW API on host '" + psHost + "' (SNI: '" + sni + "TLS handshake with IfW API on host '" + psHost + "' (SNI: '" + san + "') port '" + psPort + "' failed: " + ex.what(), start ); return; @@ -116,7 +116,7 @@ static void DoIfwNetIo( ReportIfwCheckResult( yc, checkable, command, cr, - "Certificate validation failed for IfW API on host '" + psHost + "' (SNI: '" + sni + "'; CN: " + "Certificate validation failed for IfW API on host '" + psHost + "' (SNI: '" + san + "'; CN: " + (cn.IsString() ? "'" + cn + "'" : "N/A") + ") port '" + psPort + "': " + sslConn.GetVerifyError(), start ); @@ -258,7 +258,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Array::Ptr ignoreArguments = resolveMacros("$ifw_api_ignore_arguments$"); String psHost = resolveMacros("$ifw_api_host$"); String psPort = resolveMacros("$ifw_api_port$"); - String sni = resolveMacros("$ifw_api_sni$"); + String expectedSan = resolveMacros("$ifw_api_expected_san$"); Array::Ptr sniDenylist = resolveMacros("$ifw_api_sni_denylist$"); String cert = resolveMacros("$ifw_api_cert$", &missingCert); String key = resolveMacros("$ifw_api_key$", &missingKey); @@ -334,8 +334,8 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (!checkableTimeout.IsEmpty()) checkTimeout = checkableTimeout; - if (sniDenylist->Contains(sni)) { - sni = IcingaApplication::GetInstance()->GetNodeName(); + if (sniDenylist->Contains(expectedSan)) { + expectedSan = IcingaApplication::GetInstance()->GetNodeName(); } if (resolvedMacros && !useResolvedMacros) @@ -381,11 +381,11 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes return; } - auto conn (Shared::Make(io, *ctx, sni)); + auto conn (Shared::Make(io, *ctx, expectedSan)); IoEngine::SpawnCoroutine( *strand, - [strand, checkable, command, cr, psCommand, psHost, sni, psPort, conn, req, start, checkTimeout](boost::asio::yield_context yc) { + [strand, checkable, command, cr, psCommand, psHost, expectedSan, psPort, conn, req, start, checkTimeout](boost::asio::yield_context yc) { Timeout::Ptr timeout = new Timeout(strand->context(), *strand, boost::posix_time::microseconds(int64_t(checkTimeout * 1e6)), [&conn, &checkable](boost::asio::yield_context yc) { Log(LogNotice, "IfwApiCheckTask") @@ -399,7 +399,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Defer cancelTimeout ([&timeout]() { timeout->Cancel(); }); - DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, sni, psPort, *conn, *req, start); + DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, expectedSan, psPort, *conn, *req, start); } ); } From c7e8973a1b290efd457666dcd47bcffbd8788882 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 11 Apr 2023 17:29:46 +0200 Subject: [PATCH 54/97] vars.ifw_api_localhost_addresses, not ifw_api_sni_denylist --- itl/command-icinga.conf | 2 +- lib/methods/ifwapichecktask.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index f515ad89011..a503b3c06cc 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -49,5 +49,5 @@ object CheckCommand "ifw-api" { vars.ifw_api_host = "localhost" vars.ifw_api_port = 5668 vars.ifw_api_expected_san = "$ifw_api_host$" - vars.ifw_api_sni_denylist = [ "localhost" ] + vars.ifw_api_localhost_addresses = [ "localhost" ] } diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index de352a06691..8bbb6a04719 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -259,7 +259,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes String psHost = resolveMacros("$ifw_api_host$"); String psPort = resolveMacros("$ifw_api_port$"); String expectedSan = resolveMacros("$ifw_api_expected_san$"); - Array::Ptr sniDenylist = resolveMacros("$ifw_api_sni_denylist$"); + Array::Ptr localhostAddresses = resolveMacros("$ifw_api_localhost_addresses$"); String cert = resolveMacros("$ifw_api_cert$", &missingCert); String key = resolveMacros("$ifw_api_key$", &missingKey); String ca = resolveMacros("$ifw_api_ca$", &missingCa); @@ -334,7 +334,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (!checkableTimeout.IsEmpty()) checkTimeout = checkableTimeout; - if (sniDenylist->Contains(expectedSan)) { + if (localhostAddresses->Contains(expectedSan)) { expectedSan = IcingaApplication::GetInstance()->GetNodeName(); } From d1cac00c5384c9139ba20679ccad764bf93448d3 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 11 Apr 2023 17:31:11 +0200 Subject: [PATCH 55/97] More vars.ifw_api_localhost_addresses --- itl/command-icinga.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index a503b3c06cc..79dcad7fd5a 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -49,5 +49,5 @@ object CheckCommand "ifw-api" { vars.ifw_api_host = "localhost" vars.ifw_api_port = 5668 vars.ifw_api_expected_san = "$ifw_api_host$" - vars.ifw_api_localhost_addresses = [ "localhost" ] + vars.ifw_api_localhost_addresses = [ "localhost", "127.0.0.1", "::1" ] } From 6d4b162c9a11179c3fc61dd06022f97d3e42150b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 2 May 2023 12:10:48 +0200 Subject: [PATCH 56/97] WIP --- itl/command-icinga.conf | 3 +-- lib/methods/ifwapichecktask.cpp | 13 ++++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index 79dcad7fd5a..2e990e755ba 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -46,8 +46,7 @@ object CheckCommand "ifw-api" { vars.ifw_api_command = "$command.name$" vars.ifw_api_arguments = "$command.arguments$" vars.ifw_api_ignore_arguments = [ "-C", "-ExecutionPolicy", "-NoLogo", "-NoProfile" ] - vars.ifw_api_host = "localhost" + vars.ifw_api_host = "" vars.ifw_api_port = 5668 vars.ifw_api_expected_san = "$ifw_api_host$" - vars.ifw_api_localhost_addresses = [ "localhost", "127.0.0.1", "::1" ] } diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 8bbb6a04719..5c619b6989d 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -259,7 +259,6 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes String psHost = resolveMacros("$ifw_api_host$"); String psPort = resolveMacros("$ifw_api_port$"); String expectedSan = resolveMacros("$ifw_api_expected_san$"); - Array::Ptr localhostAddresses = resolveMacros("$ifw_api_localhost_addresses$"); String cert = resolveMacros("$ifw_api_cert$", &missingCert); String key = resolveMacros("$ifw_api_key$", &missingKey); String ca = resolveMacros("$ifw_api_ca$", &missingCa); @@ -334,13 +333,17 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (!checkableTimeout.IsEmpty()) checkTimeout = checkableTimeout; - if (localhostAddresses->Contains(expectedSan)) { - expectedSan = IcingaApplication::GetInstance()->GetNodeName(); - } - if (resolvedMacros && !useResolvedMacros) return; + if (psHost.IsEmpty()) { + psHost = "localhost"; + } + + if (expectedSan.IsEmpty()) { + expectedSan = IcingaApplication::GetInstance()->GetNodeName(); + } + if (!missingCert.IsEmpty()) { cert = ApiListener::GetDefaultCertPath(); } From 50bf2dbfd51290a95ffff812c1ba7ac678e2dd8b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 3 May 2023 11:33:25 +0200 Subject: [PATCH 57/97] Check ALL CVs for "", not for missing --- itl/command-icinga.conf | 6 ++++++ lib/methods/ifwapichecktask.cpp | 26 ++++++++++++-------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index 2e990e755ba..78e1ffbf79a 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -49,4 +49,10 @@ object CheckCommand "ifw-api" { vars.ifw_api_host = "" vars.ifw_api_port = 5668 vars.ifw_api_expected_san = "$ifw_api_host$" + vars.ifw_api_cert = "" + vars.ifw_api_key = "" + vars.ifw_api_ca = "" + vars.ifw_api_crl = "" + vars.ifw_api_username = "" + vars.ifw_api_password = "" } diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 5c619b6989d..c5fa9c6e4c8 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -245,26 +245,24 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes resolvers.emplace_back("host", host); resolvers.emplace_back("command", command); - auto resolveMacros ([&resolvers, &lcr, &resolvedMacros, useResolvedMacros](const char* macros, String* missingMacro = nullptr) -> Value { + auto resolveMacros ([&resolvers, &lcr, &resolvedMacros, useResolvedMacros](const char* macros) -> Value { return MacroProcessor::ResolveMacros( - macros, resolvers, lcr, missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros + macros, resolvers, lcr, nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros ); }); - String missingCert, missingKey, missingCa, missingCrl, missingUsername, missingPassword; - String psCommand = resolveMacros("$ifw_api_command$"); Dictionary::Ptr arguments = resolveMacros("$ifw_api_arguments$"); Array::Ptr ignoreArguments = resolveMacros("$ifw_api_ignore_arguments$"); String psHost = resolveMacros("$ifw_api_host$"); String psPort = resolveMacros("$ifw_api_port$"); String expectedSan = resolveMacros("$ifw_api_expected_san$"); - String cert = resolveMacros("$ifw_api_cert$", &missingCert); - String key = resolveMacros("$ifw_api_key$", &missingKey); - String ca = resolveMacros("$ifw_api_ca$", &missingCa); - String crl = resolveMacros("$ifw_api_crl$", &missingCrl); - String username = resolveMacros("$ifw_api_username$", &missingUsername); - String password = resolveMacros("$ifw_api_password$", &missingPassword); + String cert = resolveMacros("$ifw_api_cert$"); + String key = resolveMacros("$ifw_api_key$"); + String ca = resolveMacros("$ifw_api_ca$"); + String crl = resolveMacros("$ifw_api_crl$"); + String username = resolveMacros("$ifw_api_username$"); + String password = resolveMacros("$ifw_api_password$"); Dictionary::Ptr params = new Dictionary(); @@ -344,15 +342,15 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes expectedSan = IcingaApplication::GetInstance()->GetNodeName(); } - if (!missingCert.IsEmpty()) { + if (cert.IsEmpty()) { cert = ApiListener::GetDefaultCertPath(); } - if (!missingKey.IsEmpty()) { + if (key.IsEmpty()) { key = ApiListener::GetDefaultKeyPath(); } - if (!missingCa.IsEmpty()) { + if (ca.IsEmpty()) { ca = ApiListener::GetDefaultCaPath(); } @@ -368,7 +366,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes req->set(field::content_type, "application/json"); req->body() = JsonEncode(params); - if (missingUsername.IsEmpty() && missingPassword.IsEmpty()) { + if (!username.IsEmpty() && !password.IsEmpty()) { req->set(field::authorization, "Basic " + Base64::Encode(username + ":" + password)); } From a582b4a67682828cbd931e2fc510ca12ab2fe9ee Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 3 May 2023 14:19:35 +0200 Subject: [PATCH 58/97] Better defaults --- itl/command-icinga.conf | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index 78e1ffbf79a..e35b0129ee4 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -46,13 +46,13 @@ object CheckCommand "ifw-api" { vars.ifw_api_command = "$command.name$" vars.ifw_api_arguments = "$command.arguments$" vars.ifw_api_ignore_arguments = [ "-C", "-ExecutionPolicy", "-NoLogo", "-NoProfile" ] - vars.ifw_api_host = "" + vars.ifw_api_host = null vars.ifw_api_port = 5668 vars.ifw_api_expected_san = "$ifw_api_host$" - vars.ifw_api_cert = "" - vars.ifw_api_key = "" - vars.ifw_api_ca = "" - vars.ifw_api_crl = "" - vars.ifw_api_username = "" - vars.ifw_api_password = "" + vars.ifw_api_cert = null + vars.ifw_api_key = null + vars.ifw_api_ca = null + vars.ifw_api_crl = null + vars.ifw_api_username = null + vars.ifw_api_password = null } From e3c0e2dbd99d296228bf0ed2e5b00dc5ba50080e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 3 May 2023 18:05:09 +0200 Subject: [PATCH 59/97] docs --- doc/10-icinga-template-library.md | 50 +++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index b8775c74bce..4b35a65f077 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -190,6 +190,56 @@ sleep\_time | **Optional.** The duration of the sleep in seconds. Defaults t +### ifw-api + +Built-in check command for executing arbitrary PowerShell check commands via the +[Icinga for Windows REST API](https://icinga.com/docs/icinga-for-windows/latest/doc/110-Installation/30-API-Check-Forwarder/). + +That feature lets the PowerShell processes spawned by Icinga just talk +to the pre-loaded IfW API instead of loading all PowerShell check commands +by itself on every check. In contrast the `ifw-api` command doesn't even spawn +any process, but communicates directly with the IfW API. + +Ideally you run the Icinga Director and have imported our PowerShell check commands from +[their basket](https://icinga.com/docs/icinga-for-windows/latest/doc/200-Icinga-Integration/01-Director-Baskets/). +In this case adding `ifw-api` to the imports +of the _PowerShell Base_ check command is enough to enable `ifw-api` globally. + +But the `ifw-api` command may be also used like e.g. [check_by_ssh](#plugin-check-command-by-ssh). +Its custom variables provide high flexibility. +From using a custom CA to controlling the IfW API directly from a Linux satellite. + +Optional custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Default | Description +---------------------------|-----------------------|----------------- +ifw\_api\_command | `$command.name$` | Command to run. +ifw\_api\_arguments | `$command.arguments$` | Arguments for the command as in check\_by\_ssh. +ifw\_api\_ignore_arguments | IfW-specific array | Arguments from ifw\_api\_arguments not to pass. +ifw\_api\_host | null (localhost) | IfW API host. +ifw\_api\_port | 5668 | IfW API port. +ifw\_api\_expected_san | `$ifw_api_host$` | Peer TLS certificate SAN (and SNI). null means agent NodeName. +ifw\_api\_cert | null (Icinga PKI) | TLS client certificate path. +ifw\_api\_key | null (Icinga PKI) | TLS client private key path. +ifw\_api\_ca | null (Icinga PKI) | Peer TLS CA certificate path. +ifw\_api\_crl | null (Icinga PKI) | Path to TLS CRL to check peer against. +ifw\_api\_username | null (none) | Basic auth username. +ifw\_api\_password | null (none) | Basic auth password. + +Just importing `ifw-api` into _PowerShell Base_ works because: + +* `$command.name$` is resolved at runtime to the name of the specific + check command being run and not any of the templates it imports, i.e. it + becomes e.g. "Invoke-IcingaCheckCPU", not "PowerShell Base" or even "ifw-api" +* `$command.arguments$` is resolved at runtime just like `$command.name$` +* `$command.arguments$` includes arguments needed by the vanilla + PowerShell Base, but unsuitable for the IfW API, so + ifw\_api\_ignore_arguments lists them not to pass them +* By default `ifw-api` connects to localhost, but expects the peer to identify + itself via TLS with the NodeName of the endpoint actually running the command +* ifw\_api\_cert, ifw\_api\_key, ifw\_api\_ca and ifw\_api\_crl + are also resolved on the command endpoint + ## Plugin Check Commands for Monitoring Plugins The Plugin Check Commands provides example configuration for plugin check commands From 0bd7324682ce1d45ffe1d830dd4d046af178e549 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 May 2023 12:56:16 +0200 Subject: [PATCH 60/97] docs: less check\_by\_ssh --- doc/10-icinga-template-library.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 4b35a65f077..740ba404e7a 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -214,7 +214,7 @@ Optional custom variables passed as [command parameters](03-monitoring-basics.md Name | Default | Description ---------------------------|-----------------------|----------------- ifw\_api\_command | `$command.name$` | Command to run. -ifw\_api\_arguments | `$command.arguments$` | Arguments for the command as in check\_by\_ssh. +ifw\_api\_arguments | `$command.arguments$` | Arguments for the command as in [CheckCommand](09-object-types.md#objecttype-checkcommand)#arguments. ifw\_api\_ignore_arguments | IfW-specific array | Arguments from ifw\_api\_arguments not to pass. ifw\_api\_host | null (localhost) | IfW API host. ifw\_api\_port | 5668 | IfW API port. From 1b7dc9ae04cd6217623d3acf217028223a9895b1 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 May 2023 13:02:39 +0200 Subject: [PATCH 61/97] docs: fix resolved on the command endpoint confusion --- doc/10-icinga-template-library.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 740ba404e7a..18a4ef79b21 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -237,8 +237,8 @@ Just importing `ifw-api` into _PowerShell Base_ works because: ifw\_api\_ignore_arguments lists them not to pass them * By default `ifw-api` connects to localhost, but expects the peer to identify itself via TLS with the NodeName of the endpoint actually running the command -* ifw\_api\_cert, ifw\_api\_key, ifw\_api\_ca and ifw\_api\_crl - are also resolved on the command endpoint +* The actual values of ifw\_api\_cert, ifw\_api\_key, ifw\_api\_ca and ifw\_api\_crl + are also resolved on the command endpoint if null (default) ## Plugin Check Commands for Monitoring Plugins From d3a21d9716fb0b510aac8d392b1de584261063a7 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 May 2023 13:09:59 +0200 Subject: [PATCH 62/97] Mache Doku einfach geil --- doc/10-icinga-template-library.md | 40 ++++++++++++++++--------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 18a4ef79b21..381129176f9 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -211,22 +211,23 @@ From using a custom CA to controlling the IfW API directly from a Linux satellit Optional custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): -Name | Default | Description ----------------------------|-----------------------|----------------- -ifw\_api\_command | `$command.name$` | Command to run. -ifw\_api\_arguments | `$command.arguments$` | Arguments for the command as in [CheckCommand](09-object-types.md#objecttype-checkcommand)#arguments. -ifw\_api\_ignore_arguments | IfW-specific array | Arguments from ifw\_api\_arguments not to pass. -ifw\_api\_host | null (localhost) | IfW API host. -ifw\_api\_port | 5668 | IfW API port. -ifw\_api\_expected_san | `$ifw_api_host$` | Peer TLS certificate SAN (and SNI). null means agent NodeName. -ifw\_api\_cert | null (Icinga PKI) | TLS client certificate path. -ifw\_api\_key | null (Icinga PKI) | TLS client private key path. -ifw\_api\_ca | null (Icinga PKI) | Peer TLS CA certificate path. -ifw\_api\_crl | null (Icinga PKI) | Path to TLS CRL to check peer against. -ifw\_api\_username | null (none) | Basic auth username. -ifw\_api\_password | null (none) | Basic auth password. - -Just importing `ifw-api` into _PowerShell Base_ works because: +Name | Default | Description +----------------------------|-----------------------|----------------- +ifw\_api\_command | `$command.name$` | Command to run. +ifw\_api\_arguments | `$command.arguments$` | Arguments for the command as in [CheckCommand](09-object-types.md#objecttype-checkcommand)#arguments. +ifw\_api\_ignore\_arguments | IfW-specific array | Arguments from ifw\_api\_arguments not to pass. +ifw\_api\_host | null (localhost) | IfW API host. +ifw\_api\_port | 5668 | IfW API port. +ifw\_api\_expected\_san | `$ifw_api_host$` | Peer TLS certificate SAN (and SNI). null means agent NodeName. +ifw\_api\_cert | null (Icinga PKI) | TLS client certificate path. +ifw\_api\_key | null (Icinga PKI) | TLS client private key path. +ifw\_api\_ca | null (Icinga PKI) | Peer TLS CA certificate path. +ifw\_api\_crl | null (Icinga PKI) | Path to TLS CRL to check peer against. +ifw\_api\_username | null (none) | Basic auth username. +ifw\_api\_password | null (none) | Basic auth password. + +The above defaults allow enabling `ifw-api` globally by importing it into _PowerShell Base_ +without additional configuration elsewhere: * `$command.name$` is resolved at runtime to the name of the specific check command being run and not any of the templates it imports, i.e. it @@ -235,10 +236,11 @@ Just importing `ifw-api` into _PowerShell Base_ works because: * `$command.arguments$` includes arguments needed by the vanilla PowerShell Base, but unsuitable for the IfW API, so ifw\_api\_ignore_arguments lists them not to pass them -* By default `ifw-api` connects to localhost, but expects the peer to identify - itself via TLS with the NodeName of the endpoint actually running the command +* `ifw-api` connects to localhost (if ifw\_api\_host is null), but expects + the peer to identify itself via TLS with the NodeName of the endpoint + actually running the command (if ifw\_api\_expected\_san is null) * The actual values of ifw\_api\_cert, ifw\_api\_key, ifw\_api\_ca and ifw\_api\_crl - are also resolved on the command endpoint if null (default) + are also resolved on the command endpoint if null ## Plugin Check Commands for Monitoring Plugins From b868471dc7fda2ecbf5d5ea55daac410b97842fb Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Jun 2023 11:32:59 +0200 Subject: [PATCH 63/97] Remove ifw_api_ignore_arguments --- doc/10-icinga-template-library.md | 27 +++++++++++++-------------- itl/command-icinga.conf | 1 - lib/methods/ifwapichecktask.cpp | 6 ------ 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 381129176f9..56e1de1c93e 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -211,20 +211,19 @@ From using a custom CA to controlling the IfW API directly from a Linux satellit Optional custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): -Name | Default | Description -----------------------------|-----------------------|----------------- -ifw\_api\_command | `$command.name$` | Command to run. -ifw\_api\_arguments | `$command.arguments$` | Arguments for the command as in [CheckCommand](09-object-types.md#objecttype-checkcommand)#arguments. -ifw\_api\_ignore\_arguments | IfW-specific array | Arguments from ifw\_api\_arguments not to pass. -ifw\_api\_host | null (localhost) | IfW API host. -ifw\_api\_port | 5668 | IfW API port. -ifw\_api\_expected\_san | `$ifw_api_host$` | Peer TLS certificate SAN (and SNI). null means agent NodeName. -ifw\_api\_cert | null (Icinga PKI) | TLS client certificate path. -ifw\_api\_key | null (Icinga PKI) | TLS client private key path. -ifw\_api\_ca | null (Icinga PKI) | Peer TLS CA certificate path. -ifw\_api\_crl | null (Icinga PKI) | Path to TLS CRL to check peer against. -ifw\_api\_username | null (none) | Basic auth username. -ifw\_api\_password | null (none) | Basic auth password. +Name | Default | Description +------------------------|-----------------------|----------------- +ifw\_api\_command | `$command.name$` | Command to run. +ifw\_api\_arguments | `$command.arguments$` | Arguments for the command as in [CheckCommand](09-object-types.md#objecttype-checkcommand)#arguments. +ifw\_api\_host | null (localhost) | IfW API host. +ifw\_api\_port | 5668 | IfW API port. +ifw\_api\_expected\_san | `$ifw_api_host$` | Peer TLS certificate SAN (and SNI). null means agent NodeName. +ifw\_api\_cert | null (Icinga PKI) | TLS client certificate path. +ifw\_api\_key | null (Icinga PKI) | TLS client private key path. +ifw\_api\_ca | null (Icinga PKI) | Peer TLS CA certificate path. +ifw\_api\_crl | null (Icinga PKI) | Path to TLS CRL to check peer against. +ifw\_api\_username | null (none) | Basic auth username. +ifw\_api\_password | null (none) | Basic auth password. The above defaults allow enabling `ifw-api` globally by importing it into _PowerShell Base_ without additional configuration elsewhere: diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index e35b0129ee4..b7802010d6d 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -45,7 +45,6 @@ object CheckCommand "ifw-api" { vars.ifw_api_command = "$command.name$" vars.ifw_api_arguments = "$command.arguments$" - vars.ifw_api_ignore_arguments = [ "-C", "-ExecutionPolicy", "-NoLogo", "-NoProfile" ] vars.ifw_api_host = null vars.ifw_api_port = 5668 vars.ifw_api_expected_san = "$ifw_api_host$" diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index c5fa9c6e4c8..7acd7caf209 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -253,7 +253,6 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes String psCommand = resolveMacros("$ifw_api_command$"); Dictionary::Ptr arguments = resolveMacros("$ifw_api_arguments$"); - Array::Ptr ignoreArguments = resolveMacros("$ifw_api_ignore_arguments$"); String psHost = resolveMacros("$ifw_api_host$"); String psPort = resolveMacros("$ifw_api_port$"); String expectedSan = resolveMacros("$ifw_api_expected_san$"); @@ -267,15 +266,10 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Dictionary::Ptr params = new Dictionary(); if (arguments) { - auto ignore (ignoreArguments->ToSet()); ObjectLock oLock (arguments); Array::Ptr emptyCmd = new Array(); for (auto& kv : arguments) { - if (ignore.find(kv.first) != ignore.end()) { - continue; - } - Dictionary::Ptr argSpec; if (kv.second.IsObjectType()) { From 8f9454cbdd9fdb30784e2d35fcd8b18801401519 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Jun 2023 11:35:36 +0200 Subject: [PATCH 64/97] Remove ifw_api_ignore_arguments II --- doc/10-icinga-template-library.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 56e1de1c93e..4a8c45f882f 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -232,9 +232,6 @@ without additional configuration elsewhere: check command being run and not any of the templates it imports, i.e. it becomes e.g. "Invoke-IcingaCheckCPU", not "PowerShell Base" or even "ifw-api" * `$command.arguments$` is resolved at runtime just like `$command.name$` -* `$command.arguments$` includes arguments needed by the vanilla - PowerShell Base, but unsuitable for the IfW API, so - ifw\_api\_ignore_arguments lists them not to pass them * `ifw-api` connects to localhost (if ifw\_api\_host is null), but expects the peer to identify itself via TLS with the NodeName of the endpoint actually running the command (if ifw\_api\_expected\_san is null) From 9309d510ed8c1e4773a8d66ad7d02cf53a0f7e02 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 5 Jun 2023 13:01:16 +0200 Subject: [PATCH 65/97] curl for debug --- lib/methods/ifwapichecktask.cpp | 78 ++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 7acd7caf209..2922b5dc846 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -30,8 +30,8 @@ using namespace icinga; REGISTER_FUNCTION_NONCONST(Internal, IfwApiCheck, &IfwApiCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); static void ReportIfwCheckResult( - const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, const CheckResult::Ptr& cr, const String& output, - double start, double end, int exitcode = 3, const Array::Ptr& perfdata = nullptr + const Checkable::Ptr& checkable, const Array::Ptr& cmdLine, const CheckResult::Ptr& cr, + const String& output, double start, double end, int exitcode = 3, const Array::Ptr& perfdata = nullptr ) { if (Checkable::ExecuteCommandProcessFinishedHandler) { @@ -42,7 +42,7 @@ static void ReportIfwCheckResult( pr.ExecutionEnd = end; pr.ExitStatus = exitcode; - Checkable::ExecuteCommandProcessFinishedHandler(command->GetName(), pr); + Checkable::ExecuteCommandProcessFinishedHandler(cmdLine, pr); } else { cr->SetOutput(output); cr->SetPerformanceData(perfdata); @@ -50,27 +50,27 @@ static void ReportIfwCheckResult( cr->SetExitStatus(exitcode); cr->SetExecutionStart(start); cr->SetExecutionEnd(end); - cr->SetCommand(command->GetName()); + cr->SetCommand(cmdLine); checkable->ProcessCheckResult(cr); } } static void ReportIfwCheckResult( - boost::asio::yield_context yc, const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, + boost::asio::yield_context yc, const Checkable::Ptr& checkable, const Array::Ptr& cmdLine, const CheckResult::Ptr& cr, const String& output, double start ) { double end = Utility::GetTime(); CpuBoundWork cbw (yc); - Utility::QueueAsyncCallback([checkable, command, cr, output, start, end]() { - ReportIfwCheckResult(checkable, command, cr, output, start, end); + Utility::QueueAsyncCallback([checkable, cmdLine, cr, output, start, end]() { + ReportIfwCheckResult(checkable, cmdLine, cr, output, start, end); }); } static void DoIfwNetIo( - boost::asio::yield_context yc, const Checkable::Ptr& checkable, const CheckCommand::Ptr& command, + boost::asio::yield_context yc, const Checkable::Ptr& checkable, const Array::Ptr& cmdLine, const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& san, const String& psPort, AsioTlsStream& conn, boost::beast::http::request& req, double start ) @@ -86,7 +86,7 @@ static void DoIfwNetIo( Connect(conn.lowest_layer(), psHost, psPort, yc); } catch (const std::exception& ex) { ReportIfwCheckResult( - yc, checkable, command, cr, + yc, checkable, cmdLine, cr, "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start ); return; @@ -98,7 +98,7 @@ static void DoIfwNetIo( sslConn.async_handshake(conn.next_layer().client, yc); } catch (const std::exception& ex) { ReportIfwCheckResult( - yc, checkable, command, cr, + yc, checkable, cmdLine, cr, "TLS handshake with IfW API on host '" + psHost + "' (SNI: '" + san + "') port '" + psPort + "' failed: " + ex.what(), start ); @@ -115,7 +115,7 @@ static void DoIfwNetIo( } ReportIfwCheckResult( - yc, checkable, command, cr, + yc, checkable, cmdLine, cr, "Certificate validation failed for IfW API on host '" + psHost + "' (SNI: '" + san + "'; CN: " + (cn.IsString() ? "'" + cn + "'" : "N/A") + ") port '" + psPort + "': " + sslConn.GetVerifyError(), start @@ -128,7 +128,7 @@ static void DoIfwNetIo( conn.async_flush(yc); } catch (const std::exception& ex) { ReportIfwCheckResult( - yc, checkable, command, cr, + yc, checkable, cmdLine, cr, "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start ); return; @@ -138,7 +138,7 @@ static void DoIfwNetIo( async_read(conn, buf, resp, yc); } catch (const std::exception& ex) { ReportIfwCheckResult( - yc, checkable, command, cr, + yc, checkable, cmdLine, cr, "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start ); return; @@ -158,7 +158,7 @@ static void DoIfwNetIo( jsonRoot = JsonDecode(resp.body()); } catch (const std::exception& ex) { ReportIfwCheckResult( - checkable, command, cr, + checkable, cmdLine, cr, "Got bad JSON from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start, end ); return; @@ -166,7 +166,7 @@ static void DoIfwNetIo( if (!jsonRoot.IsObjectType()) { ReportIfwCheckResult( - checkable, command, cr, + checkable, cmdLine, cr, "Got JSON, but not an object, from IfW API on host '" + psHost + "' port '" + psPort + "'", start, end ); return; @@ -176,7 +176,7 @@ static void DoIfwNetIo( if (!Dictionary::Ptr(jsonRoot)->Get(psCommand, &jsonBranch)) { ReportIfwCheckResult( - checkable, command, cr, + checkable, cmdLine, cr, "Missing ." + psCommand + " in JSON object from IfW API on host '" + psHost + "' port '" + psPort + "'", start, end ); return; @@ -184,7 +184,7 @@ static void DoIfwNetIo( if (!jsonBranch.IsObjectType()) { ReportIfwCheckResult( - checkable, command, cr, + checkable, cmdLine, cr, "." + psCommand + " is not an object in JSON from IfW API on host '" + psHost + "' port '" + psPort + "'", start, end ); return; @@ -197,7 +197,7 @@ static void DoIfwNetIo( exitcode = result->Get("exitcode"); } catch (const std::exception& ex) { ReportIfwCheckResult( - checkable, command, cr, + checkable, cmdLine, cr, "Got bad exitcode from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start, end ); return; @@ -209,13 +209,13 @@ static void DoIfwNetIo( perfdata = result->Get("perfdata"); } catch (const std::exception& ex) { ReportIfwCheckResult( - checkable, command, cr, + checkable, cmdLine, cr, "Got bad perfdata from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start, end ); return; } - ReportIfwCheckResult(checkable, command, cr, result->Get("checkresult"), start, end, exitcode, perfdata); + ReportIfwCheckResult(checkable, cmdLine, cr, result->Get("checkresult"), start, end, exitcode, perfdata); } void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, @@ -353,15 +353,41 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes uri->SetPath({ "v1", "checker" }); uri->SetQuery({{ "command", psCommand }}); + auto relative (uri->Format()); + auto body (JsonEncode(params)); auto req (Shared>::Make()); req->method(verb::post); - req->target(uri->Format()); + req->target(relative); req->set(field::content_type, "application/json"); - req->body() = JsonEncode(params); + req->body() = body; + + static auto curlTlsMinVersion ((String("--") + DEFAULT_TLS_PROTOCOLMIN).ToLower()); + + Array::Ptr cmdLine = new Array({ + "curl", "--silent", "--show-error", curlTlsMinVersion, "--fail", + "--resolve", expectedSan + ":" + psPort + ":" + psHost, + "--ciphers", DEFAULT_TLS_CIPHERS, + "--cert", cert, + "--key", key, + "--cacert", ca, + "--request", "POST", + "--url", "https://" + expectedSan + ":" + psPort + relative, + "--header", "Content-Type: application/json", + "--data-raw", body + }); + + if (!crl.IsEmpty()) { + cmdLine->Add("--crlfile"); + cmdLine->Add(crl); + } if (!username.IsEmpty() && !password.IsEmpty()) { - req->set(field::authorization, "Basic " + Base64::Encode(username + ":" + password)); + auto authn (username + ":" + password); + + req->set(field::authorization, "Basic " + Base64::Encode(authn)); + cmdLine->Add("--user"); + cmdLine->Add(authn); } auto& io (IoEngine::Get().GetIoContext()); @@ -372,7 +398,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes try { ctx = SetupSslContext(cert, key, ca, crl, DEFAULT_TLS_CIPHERS, DEFAULT_TLS_PROTOCOLMIN, DebugInfo()); } catch (const std::exception& ex) { - ReportIfwCheckResult(checkable, command, cr, ex.what(), start, Utility::GetTime()); + ReportIfwCheckResult(checkable, cmdLine, cr, ex.what(), start, Utility::GetTime()); return; } @@ -380,7 +406,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes IoEngine::SpawnCoroutine( *strand, - [strand, checkable, command, cr, psCommand, psHost, expectedSan, psPort, conn, req, start, checkTimeout](boost::asio::yield_context yc) { + [strand, checkable, cmdLine, cr, psCommand, psHost, expectedSan, psPort, conn, req, start, checkTimeout](boost::asio::yield_context yc) { Timeout::Ptr timeout = new Timeout(strand->context(), *strand, boost::posix_time::microseconds(int64_t(checkTimeout * 1e6)), [&conn, &checkable](boost::asio::yield_context yc) { Log(LogNotice, "IfwApiCheckTask") @@ -394,7 +420,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Defer cancelTimeout ([&timeout]() { timeout->Cancel(); }); - DoIfwNetIo(yc, checkable, command, cr, psCommand, psHost, expectedSan, psPort, *conn, *req, start); + DoIfwNetIo(yc, checkable, cmdLine, cr, psCommand, psHost, expectedSan, psPort, *conn, *req, start); } ); } From 40859ac938befe9babd0bba3b7703630f8dba221 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 7 Jun 2023 16:00:42 +0200 Subject: [PATCH 66/97] ifw\_api\_arguments must not directly contain functions --- doc/10-icinga-template-library.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 4a8c45f882f..96be85249de 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -225,6 +225,13 @@ ifw\_api\_crl | null (Icinga PKI) | Path to TLS CRL to check peer ifw\_api\_username | null (none) | Basic auth username. ifw\_api\_password | null (none) | Basic auth password. +!!! warning + + Due to how Icinga 2 resolves macros and serializes the resolved values for + sending to a command endpoint (if any), ifw\_api\_arguments must not directly + contain functions in case `ifw-api` is used with command endpoints! Only + macro strings referring to custom variables which are set to functions work. + The above defaults allow enabling `ifw-api` globally by importing it into _PowerShell Base_ without additional configuration elsewhere: From f0dcdc87e61716e188435933695ec1bd4873962f Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 7 Jun 2023 16:40:56 +0200 Subject: [PATCH 67/97] make IDE happy --- doc/10-icinga-template-library.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 96be85249de..9092dfa0f00 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -211,19 +211,19 @@ From using a custom CA to controlling the IfW API directly from a Linux satellit Optional custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): -Name | Default | Description -------------------------|-----------------------|----------------- -ifw\_api\_command | `$command.name$` | Command to run. -ifw\_api\_arguments | `$command.arguments$` | Arguments for the command as in [CheckCommand](09-object-types.md#objecttype-checkcommand)#arguments. -ifw\_api\_host | null (localhost) | IfW API host. -ifw\_api\_port | 5668 | IfW API port. -ifw\_api\_expected\_san | `$ifw_api_host$` | Peer TLS certificate SAN (and SNI). null means agent NodeName. -ifw\_api\_cert | null (Icinga PKI) | TLS client certificate path. -ifw\_api\_key | null (Icinga PKI) | TLS client private key path. -ifw\_api\_ca | null (Icinga PKI) | Peer TLS CA certificate path. -ifw\_api\_crl | null (Icinga PKI) | Path to TLS CRL to check peer against. -ifw\_api\_username | null (none) | Basic auth username. -ifw\_api\_password | null (none) | Basic auth password. +| Name | Default | Description | +|-------------------------|-----------------------|-------------------------------------------------------------------------------------------------------| +| ifw\_api\_command | `$command.name$` | Command to run. | +| ifw\_api\_arguments | `$command.arguments$` | Arguments for the command as in [CheckCommand](09-object-types.md#objecttype-checkcommand)#arguments. | +| ifw\_api\_host | null (localhost) | IfW API host. | +| ifw\_api\_port | 5668 | IfW API port. | +| ifw\_api\_expected\_san | `$ifw_api_host$` | Peer TLS certificate SAN (and SNI). null means agent NodeName. | +| ifw\_api\_cert | null (Icinga PKI) | TLS client certificate path. | +| ifw\_api\_key | null (Icinga PKI) | TLS client private key path. | +| ifw\_api\_ca | null (Icinga PKI) | Peer TLS CA certificate path. | +| ifw\_api\_crl | null (Icinga PKI) | Path to TLS CRL to check peer against. | +| ifw\_api\_username | null (none) | Basic auth username. | +| ifw\_api\_password | null (none) | Basic auth password. | !!! warning From 2a378e9bbe8a6ecc0ef2c90bd31096875485563e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 7 Jun 2023 16:44:48 +0200 Subject: [PATCH 68/97] no undef. behavior? - ok. --- doc/10-icinga-template-library.md | 28 ++++++++++++++-------------- itl/command-icinga.conf | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 9092dfa0f00..127c99abcfd 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -211,19 +211,19 @@ From using a custom CA to controlling the IfW API directly from a Linux satellit Optional custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): -| Name | Default | Description | -|-------------------------|-----------------------|-------------------------------------------------------------------------------------------------------| -| ifw\_api\_command | `$command.name$` | Command to run. | -| ifw\_api\_arguments | `$command.arguments$` | Arguments for the command as in [CheckCommand](09-object-types.md#objecttype-checkcommand)#arguments. | -| ifw\_api\_host | null (localhost) | IfW API host. | -| ifw\_api\_port | 5668 | IfW API port. | -| ifw\_api\_expected\_san | `$ifw_api_host$` | Peer TLS certificate SAN (and SNI). null means agent NodeName. | -| ifw\_api\_cert | null (Icinga PKI) | TLS client certificate path. | -| ifw\_api\_key | null (Icinga PKI) | TLS client private key path. | -| ifw\_api\_ca | null (Icinga PKI) | Peer TLS CA certificate path. | -| ifw\_api\_crl | null (Icinga PKI) | Path to TLS CRL to check peer against. | -| ifw\_api\_username | null (none) | Basic auth username. | -| ifw\_api\_password | null (none) | Basic auth password. | +| Name | Default | Description | +|-------------------------|-------------------|-------------------------------------------------------------------------------------------------------------| +| ifw\_api\_command | `$command.name$` | Command to run. | +| ifw\_api\_arguments | {} (none) | Arguments for the command, similar to [CheckCommand](09-object-types.md#objecttype-checkcommand)#arguments. | +| ifw\_api\_host | null (localhost) | IfW API host. | +| ifw\_api\_port | 5668 | IfW API port. | +| ifw\_api\_expected\_san | `$ifw_api_host$` | Peer TLS certificate SAN (and SNI). null means agent NodeName. | +| ifw\_api\_cert | null (Icinga PKI) | TLS client certificate path. | +| ifw\_api\_key | null (Icinga PKI) | TLS client private key path. | +| ifw\_api\_ca | null (Icinga PKI) | Peer TLS CA certificate path. | +| ifw\_api\_crl | null (Icinga PKI) | Path to TLS CRL to check peer against. | +| ifw\_api\_username | null (none) | Basic auth username. | +| ifw\_api\_password | null (none) | Basic auth password. | !!! warning @@ -238,7 +238,7 @@ without additional configuration elsewhere: * `$command.name$` is resolved at runtime to the name of the specific check command being run and not any of the templates it imports, i.e. it becomes e.g. "Invoke-IcingaCheckCPU", not "PowerShell Base" or even "ifw-api" -* `$command.arguments$` is resolved at runtime just like `$command.name$` +* Our PowerShell framework provides ifw\_api\_arguments for all of its commands. * `ifw-api` connects to localhost (if ifw\_api\_host is null), but expects the peer to identify itself via TLS with the NodeName of the endpoint actually running the command (if ifw\_api\_expected\_san is null) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index b7802010d6d..74523a4b616 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -44,7 +44,7 @@ object CheckCommand "ifw-api" { import "ifw-api-check-command" vars.ifw_api_command = "$command.name$" - vars.ifw_api_arguments = "$command.arguments$" + vars.ifw_api_arguments = {} vars.ifw_api_host = null vars.ifw_api_port = 5668 vars.ifw_api_expected_san = "$ifw_api_host$" From cd508c2e7b282b001043818f287add545710012b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 12 Jun 2023 12:17:55 +0200 Subject: [PATCH 69/97] $ifw_api_arguments$ may not directly contain functions --- lib/methods/ifwapichecktask.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 2922b5dc846..b4de11fb104 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -30,7 +30,7 @@ using namespace icinga; REGISTER_FUNCTION_NONCONST(Internal, IfwApiCheck, &IfwApiCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); static void ReportIfwCheckResult( - const Checkable::Ptr& checkable, const Array::Ptr& cmdLine, const CheckResult::Ptr& cr, + const Checkable::Ptr& checkable, const Value& cmdLine, const CheckResult::Ptr& cr, const String& output, double start, double end, int exitcode = 3, const Array::Ptr& perfdata = nullptr ) { @@ -57,7 +57,7 @@ static void ReportIfwCheckResult( } static void ReportIfwCheckResult( - boost::asio::yield_context yc, const Checkable::Ptr& checkable, const Array::Ptr& cmdLine, + boost::asio::yield_context yc, const Checkable::Ptr& checkable, const Value& cmdLine, const CheckResult::Ptr& cr, const String& output, double start ) { @@ -281,6 +281,23 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes // See default branch of below switch argSpec->Set("repeat_key", false); + { + ObjectLock oLock (argSpec); + + for (auto& kv : argSpec) { + if (kv.second.IsObjectType()) { + auto now (Utility::GetTime()); + + ReportIfwCheckResult( + checkable, command->GetName(), cr, + "$ifw_api_arguments$ may not directly contain functions.", now, now + ); + + return; + } + } + } + /* MacroProcessor::ResolveArguments() converts * * [ "check_example" ] From 9ee3186bffa2536f4d5894ce88086202a2ac9e88 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 12 Jun 2023 12:37:11 +0200 Subject: [PATCH 70/97] docs --- doc/10-icinga-template-library.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 127c99abcfd..4e2fcae3476 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -225,11 +225,11 @@ Optional custom variables passed as [command parameters](03-monitoring-basics.md | ifw\_api\_username | null (none) | Basic auth username. | | ifw\_api\_password | null (none) | Basic auth password. | -!!! warning +!!! info Due to how Icinga 2 resolves macros and serializes the resolved values for - sending to a command endpoint (if any), ifw\_api\_arguments must not directly - contain functions in case `ifw-api` is used with command endpoints! Only + sending to a command endpoint (if any), ifw\_api\_arguments may not directly + contain functions for the case `ifw-api` is used with command endpoints. Only macro strings referring to custom variables which are set to functions work. The above defaults allow enabling `ifw-api` globally by importing it into _PowerShell Base_ From c6f33434d9a9db994a2a51c7c496d7252b7978be Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 13 Jun 2023 15:55:26 +0200 Subject: [PATCH 71/97] CheckCommand "ifw-api-if-exists" --- doc/10-icinga-template-library.md | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 4e2fcae3476..f6d32b47b96 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -202,10 +202,31 @@ any process, but communicates directly with the IfW API. Ideally you run the Icinga Director and have imported our PowerShell check commands from [their basket](https://icinga.com/docs/icinga-for-windows/latest/doc/200-Icinga-Integration/01-Director-Baskets/). -In this case adding `ifw-api` to the imports -of the _PowerShell Base_ check command is enough to enable `ifw-api` globally. +In this case adding `ifw-api` to the imports of the _PowerShell Base_ +check command is enough to enable `ifw-api` globally. -But the `ifw-api` command may be also used like e.g. [check_by_ssh](#plugin-check-command-by-ssh). +!!! warning + + Do the latter only if your **entire** Icinga cluster runs v2.14+! Otherwise, + this will break all older nodes which load the modified _PowerShell Base_ + from a global zone while the imported `ifw-api` is missing. + +For a cluster which may contain older Icinga instances there's a workaround: +Put the following command in a global zone and import it instead of `ifw-api`: + +``` +object CheckCommand "ifw-api-if-exists" { + try { + import "ifw-api" + } except { + } +} +``` + +In any case re-running the Kickstart Wizard is required +for the Director to recognise these new check commands. + +The `ifw-api` command may be also used like e.g. [check_by_ssh](#plugin-check-command-by-ssh). Its custom variables provide high flexibility. From using a custom CA to controlling the IfW API directly from a Linux satellite. @@ -232,8 +253,8 @@ Optional custom variables passed as [command parameters](03-monitoring-basics.md contain functions for the case `ifw-api` is used with command endpoints. Only macro strings referring to custom variables which are set to functions work. -The above defaults allow enabling `ifw-api` globally by importing it into _PowerShell Base_ -without additional configuration elsewhere: +The above defaults allow enabling `ifw-api(-if-exists)` globally by importing +it into _PowerShell Base_ without additional configuration elsewhere: * `$command.name$` is resolved at runtime to the name of the specific check command being run and not any of the templates it imports, i.e. it From 249bccf49a531f66546aabe5cc2f9c9864bc0c61 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 13 Jun 2023 16:03:28 +0200 Subject: [PATCH 72/97] Call PluginCheckTask::ScriptFunc() and hope for the best --- lib/methods/ifwapichecktask.cpp | 17 +++++++++++++++++ lib/remote/apilistener.cpp | 4 +++- lib/remote/apilistener.hpp | 3 ++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index b4de11fb104..cd4c9143f41 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -4,6 +4,7 @@ # include #endif /* _WIN32 */ #include "methods/ifwapichecktask.hpp" +#include "methods/pluginchecktask.hpp" #include "icinga/icingaapplication.hpp" #include "icinga/pluginutility.hpp" #include "base/base64.hpp" @@ -228,6 +229,22 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); + // We're going to just resolve macros for the actual check execution happening elsewhere + if (resolvedMacros && !useResolvedMacros) { + auto commandEndpoint (checkable->GetCommandEndpoint()); + + // There's indeed a command endpoint, obviously for the actual check execution + if (commandEndpoint) { + // But it doesn't have this function, yet ("ifw-api-check-command") + if (!(commandEndpoint->GetCapabilities() & (uint_fast64_t)ApiCapabilities::IfwApiCheckCommand)) { + // Assume "ifw-api-check-command" has been imported into a check command which can also work + // based on "plugin-check-command", delegate respectively and hope for the best + PluginCheckTask::ScriptFunc(checkable, cr, resolvedMacros, useResolvedMacros); + return; + } + } + } + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); auto lcr (checkable->GetLastCheckResult()); diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index cb122aa2146..45f9e7c7728 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -605,7 +605,9 @@ static const auto l_AppVersionInt (([]() -> unsigned long { + boost::lexical_cast(match[3].str()); })()); -static const auto l_MyCapabilities (ApiCapabilities::ExecuteArbitraryCommand); +static const auto l_MyCapabilities ( + (uint_fast64_t)ApiCapabilities::ExecuteArbitraryCommand | (uint_fast64_t)ApiCapabilities::IfwApiCheckCommand +); /** * Processes a new client connection. diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index 1be29522f85..c80c2cb3281 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -67,7 +67,8 @@ struct ConfigDirInformation */ enum class ApiCapabilities : uint_fast64_t { - ExecuteArbitraryCommand = 1u + ExecuteArbitraryCommand = 1u, + IfwApiCheckCommand = 2u }; /** From 3f126405691f7ceec55226c01636f1cbd13c7bee Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 14 Jun 2023 16:09:31 +0200 Subject: [PATCH 73/97] placebo --- lib/methods/ifwapichecktask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index cd4c9143f41..14011dcdf26 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -302,12 +302,12 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes ObjectLock oLock (argSpec); for (auto& kv : argSpec) { - if (kv.second.IsObjectType()) { + if (kv.second.GetType() == ValueObject) { auto now (Utility::GetTime()); ReportIfwCheckResult( checkable, command->GetName(), cr, - "$ifw_api_arguments$ may not directly contain functions.", now, now + "$ifw_api_arguments$ may not directly contain objects (especially functions).", now, now ); return; From 518f9553c4bf4115c314b6b063e2dfa5f31de670 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 19 Jun 2023 12:59:59 +0200 Subject: [PATCH 74/97] ApiCapabilities: 1 << x --- lib/remote/apilistener.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index c80c2cb3281..756af55867c 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -67,8 +67,8 @@ struct ConfigDirInformation */ enum class ApiCapabilities : uint_fast64_t { - ExecuteArbitraryCommand = 1u, - IfwApiCheckCommand = 2u + ExecuteArbitraryCommand = 1u << 0u, + IfwApiCheckCommand = 1u << 1u }; /** From 9b86779078cdf2cea1c6034b19c09e379d7e401d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 19 Jun 2023 13:00:17 +0200 Subject: [PATCH 75/97] ApiCapabilities: trailing , --- lib/remote/apilistener.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index 756af55867c..ffe97a2b324 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -68,7 +68,7 @@ struct ConfigDirInformation enum class ApiCapabilities : uint_fast64_t { ExecuteArbitraryCommand = 1u << 0u, - IfwApiCheckCommand = 1u << 1u + IfwApiCheckCommand = 1u << 1u, }; /** From 4fee8042c82f9c040c911ddbaea6f65ce80495be Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 19 Jun 2023 13:16:20 +0200 Subject: [PATCH 76/97] Fix curl --resolve --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 14011dcdf26..96180218005 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -400,7 +400,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Array::Ptr cmdLine = new Array({ "curl", "--silent", "--show-error", curlTlsMinVersion, "--fail", - "--resolve", expectedSan + ":" + psPort + ":" + psHost, + "--connect-to", expectedSan + ":" + psPort + ":" + psHost + ":" + psPort, "--ciphers", DEFAULT_TLS_CIPHERS, "--cert", cert, "--key", key, From a80701922c6872f55223832e1f774878c1db0136 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 19 Jun 2023 13:24:29 +0200 Subject: [PATCH 77/97] curl: --verbose, for debugging after all --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 96180218005..8770db9910e 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -399,7 +399,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes static auto curlTlsMinVersion ((String("--") + DEFAULT_TLS_PROTOCOLMIN).ToLower()); Array::Ptr cmdLine = new Array({ - "curl", "--silent", "--show-error", curlTlsMinVersion, "--fail", + "curl", "--verbose", curlTlsMinVersion, "--fail", "--connect-to", expectedSan + ":" + psPort + ":" + psHost + ":" + psPort, "--ciphers", DEFAULT_TLS_CIPHERS, "--cert", cert, From 0d57a803bc6e2dc088105798f5a3b8df677d47d3 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 19 Jun 2023 13:45:10 +0200 Subject: [PATCH 78/97] Otherwise, all older nodes which load the modified _PowerShell Base_ (e.g. from a global zone) will reject all new configuration --- doc/10-icinga-template-library.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index f6d32b47b96..7533d09424c 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -208,8 +208,8 @@ check command is enough to enable `ifw-api` globally. !!! warning Do the latter only if your **entire** Icinga cluster runs v2.14+! Otherwise, - this will break all older nodes which load the modified _PowerShell Base_ - from a global zone while the imported `ifw-api` is missing. + all older nodes which load the modified _PowerShell Base_ (e.g. from a global + zone) will reject all new configuration as the imported `ifw-api` is missing. For a cluster which may contain older Icinga instances there's a workaround: Put the following command in a global zone and import it instead of `ifw-api`: From 5e2b09985fef2db7d8c60c1f3c68d2a825bba2ac Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 19 Jun 2023 13:46:02 +0200 Subject: [PATCH 79/97] Repair --- doc/10-icinga-template-library.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 7533d09424c..8bad81b27e5 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -187,9 +187,6 @@ Name | Description ----------------|-------------- sleep\_time | **Optional.** The duration of the sleep in seconds. Defaults to 1s. - - - ### ifw-api Built-in check command for executing arbitrary PowerShell check commands via the @@ -266,6 +263,9 @@ it into _PowerShell Base_ without additional configuration elsewhere: * The actual values of ifw\_api\_cert, ifw\_api\_key, ifw\_api\_ca and ifw\_api\_crl are also resolved on the command endpoint if null + + + ## Plugin Check Commands for Monitoring Plugins The Plugin Check Commands provides example configuration for plugin check commands From 050dff41af2046c9597c2ca3ec0edfbb65b342a4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 20 Jun 2023 18:06:42 +0200 Subject: [PATCH 80/97] headers --- lib/methods/ifwapichecktask.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 8770db9910e..74a6a27d56c 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -387,14 +387,19 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes uri->SetPath({ "v1", "checker" }); uri->SetQuery({{ "command", psCommand }}); + static auto userAgent ("Icinga/" + Application::GetAppVersion()); auto relative (uri->Format()); auto body (JsonEncode(params)); auto req (Shared>::Make()); req->method(verb::post); req->target(relative); + req->set(field::accept, "application/json"); req->set(field::content_type, "application/json"); + req->set(field::host, expectedSan + ":" + psPort); + req->set(field::user_agent, userAgent); req->body() = body; + req->content_length(req->body().size()); static auto curlTlsMinVersion ((String("--") + DEFAULT_TLS_PROTOCOLMIN).ToLower()); @@ -407,6 +412,8 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes "--cacert", ca, "--request", "POST", "--url", "https://" + expectedSan + ":" + psPort + relative, + "--user-agent", userAgent, + "--header", "Accept: application/json", "--header", "Content-Type: application/json", "--data-raw", body }); From 4703b4662cdd3bbd378ba03b3e63bc10196851c9 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 21 Jun 2023 13:23:36 +0200 Subject: [PATCH 81/97] Timeout exceeded --- lib/methods/ifwapichecktask.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 74a6a27d56c..442de793ccf 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -70,6 +70,17 @@ static void ReportIfwCheckResult( }); } +static const char* GetUnderstandableError(const std::exception& ex) +{ + auto se (dynamic_cast(&ex)); + + if (se && se->code() == boost::asio::error::operation_aborted) { + return "Timeout exceeded"; + } + + return ex.what(); +} + static void DoIfwNetIo( boost::asio::yield_context yc, const Checkable::Ptr& checkable, const Array::Ptr& cmdLine, const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& san, const String& psPort, @@ -88,7 +99,8 @@ static void DoIfwNetIo( } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, cmdLine, cr, - "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start + "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + + GetUnderstandableError(ex), start ); return; } @@ -101,7 +113,7 @@ static void DoIfwNetIo( ReportIfwCheckResult( yc, checkable, cmdLine, cr, "TLS handshake with IfW API on host '" + psHost + "' (SNI: '" + san - + "') port '" + psPort + "' failed: " + ex.what(), start + + "') port '" + psPort + "' failed: " + GetUnderstandableError(ex), start ); return; } @@ -130,7 +142,8 @@ static void DoIfwNetIo( } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, cmdLine, cr, - "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start + "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + + GetUnderstandableError(ex), start ); return; } @@ -140,7 +153,8 @@ static void DoIfwNetIo( } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, cmdLine, cr, - "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start + "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + + GetUnderstandableError(ex), start ); return; } From ced0427868683d4fce6f05f05a82b0a61d3f2110 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 21 Jun 2023 14:41:51 +0200 Subject: [PATCH 82/97] Missing ." + psCommand + ".exitcode in JSON object from IfW API --- lib/methods/ifwapichecktask.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 442de793ccf..d52f3cbbe87 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -206,10 +206,22 @@ static void DoIfwNetIo( } Dictionary::Ptr result = jsonBranch; + + Value exitval; + + if (!result->Get("exitcode", &exitval)) { + ReportIfwCheckResult( + checkable, cmdLine, cr, + "Missing ." + psCommand + ".exitcode in JSON object from IfW API on host '" + + psHost + "' port '" + psPort + "'", start, end + ); + return; + } + double exitcode; try { - exitcode = result->Get("exitcode"); + exitcode = exitval; } catch (const std::exception& ex) { ReportIfwCheckResult( checkable, cmdLine, cr, From bd0a5338ce1939fd3a2ac3635de61832e04c3c36 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 21 Jun 2023 14:47:03 +0200 Subject: [PATCH 83/97] static CONST auto --- lib/methods/ifwapichecktask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index d52f3cbbe87..694a98895c5 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -413,7 +413,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes uri->SetPath({ "v1", "checker" }); uri->SetQuery({{ "command", psCommand }}); - static auto userAgent ("Icinga/" + Application::GetAppVersion()); + static const auto userAgent ("Icinga/" + Application::GetAppVersion()); auto relative (uri->Format()); auto body (JsonEncode(params)); auto req (Shared>::Make()); @@ -427,7 +427,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes req->body() = body; req->content_length(req->body().size()); - static auto curlTlsMinVersion ((String("--") + DEFAULT_TLS_PROTOCOLMIN).ToLower()); + static const auto curlTlsMinVersion ((String("--") + DEFAULT_TLS_PROTOCOLMIN).ToLower()); Array::Ptr cmdLine = new Array({ "curl", "--verbose", curlTlsMinVersion, "--fail", From 22f9e924c3502aec81dd635c82b242a574253365 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 21 Jun 2023 14:56:21 +0200 Subject: [PATCH 84/97] code style --- lib/methods/ifwapichecktask.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 694a98895c5..0187f46ea33 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -99,8 +99,8 @@ static void DoIfwNetIo( } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, cmdLine, cr, - "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " - + GetUnderstandableError(ex), start + "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + GetUnderstandableError(ex), + start ); return; } @@ -113,7 +113,8 @@ static void DoIfwNetIo( ReportIfwCheckResult( yc, checkable, cmdLine, cr, "TLS handshake with IfW API on host '" + psHost + "' (SNI: '" + san - + "') port '" + psPort + "' failed: " + GetUnderstandableError(ex), start + + "') port '" + psPort + "' failed: " + GetUnderstandableError(ex), + start ); return; } @@ -142,8 +143,8 @@ static void DoIfwNetIo( } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, cmdLine, cr, - "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " - + GetUnderstandableError(ex), start + "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + GetUnderstandableError(ex), + start ); return; } @@ -153,8 +154,8 @@ static void DoIfwNetIo( } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, cmdLine, cr, - "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " - + GetUnderstandableError(ex), start + "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + GetUnderstandableError(ex), + start ); return; } @@ -212,8 +213,8 @@ static void DoIfwNetIo( if (!result->Get("exitcode", &exitval)) { ReportIfwCheckResult( checkable, cmdLine, cr, - "Missing ." + psCommand + ".exitcode in JSON object from IfW API on host '" - + psHost + "' port '" + psPort + "'", start, end + "Missing ." + psCommand + ".exitcode in JSON object from IfW API on host '" + psHost + "' port '" + psPort + "'", + start, end ); return; } From b783ee1257568d3cd85f68da0498df48b3d9a78d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 21 Jun 2023 15:10:58 +0200 Subject: [PATCH 85/97] exitcode == {ServiceOK, ServiceWarning, ServiceCritical, ServiceUnknown} --- lib/methods/ifwapichecktask.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 0187f46ea33..d2e74f97bcb 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -5,6 +5,7 @@ #endif /* _WIN32 */ #include "methods/ifwapichecktask.hpp" #include "methods/pluginchecktask.hpp" +#include "icinga/checkresult-ti.hpp" #include "icinga/icingaapplication.hpp" #include "icinga/pluginutility.hpp" #include "base/base64.hpp" @@ -25,6 +26,7 @@ #include #include #include +#include using namespace icinga; @@ -208,9 +210,9 @@ static void DoIfwNetIo( Dictionary::Ptr result = jsonBranch; - Value exitval; + Value exitcode; - if (!result->Get("exitcode", &exitval)) { + if (!result->Get("exitcode", &exitcode)) { ReportIfwCheckResult( checkable, cmdLine, cr, "Missing ." + psCommand + ".exitcode in JSON object from IfW API on host '" + psHost + "' port '" + psPort + "'", @@ -219,14 +221,15 @@ static void DoIfwNetIo( return; } - double exitcode; + static const std::set exitcodes {ServiceOK, ServiceWarning, ServiceCritical, ServiceUnknown}; + static const auto exitcodeList (Array::FromSet(exitcodes)->Join(", ")); - try { - exitcode = exitval; - } catch (const std::exception& ex) { + if (!exitcode.IsNumber() || exitcodes.find(exitcode) == exitcodes.end()) { ReportIfwCheckResult( checkable, cmdLine, cr, - "Got bad exitcode from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start, end + "Got bad exitcode " + JsonEncode(exitcode) + " from IfW API on host '" + psHost + "' port '" + psPort + + "', expected one of: " + exitcodeList, + start, end ); return; } From 6eade04bd807989d9a1ef3b66ac82cee74e04b66 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 21 Jun 2023 15:30:13 +0200 Subject: [PATCH 86/97] perfdata: expected an array --- lib/methods/ifwapichecktask.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index d2e74f97bcb..8c6990653dd 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -234,14 +234,17 @@ static void DoIfwNetIo( return; } + auto perfdataVal (result->Get("perfdata")); Array::Ptr perfdata; try { - perfdata = result->Get("perfdata"); - } catch (const std::exception& ex) { + perfdata = perfdataVal; + } catch (const std::exception&) { ReportIfwCheckResult( checkable, cmdLine, cr, - "Got bad perfdata from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start, end + "Got bad perfdata " + JsonEncode(perfdataVal) + " from IfW API on host '" + + psHost + "' port '" + psPort + "', expected an array", + start, end ); return; } From e4f51cdc29bc8d7c87f7f766fe541e382b50511e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 21 Jun 2023 15:35:48 +0200 Subject: [PATCH 87/97] expected an array of strings --- lib/methods/ifwapichecktask.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 8c6990653dd..988bac9df3b 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -249,6 +249,22 @@ static void DoIfwNetIo( return; } + if (perfdata) { + ObjectLock oLock (perfdata); + + for (auto& pv : perfdata) { + if (!pv.IsString()) { + ReportIfwCheckResult( + checkable, cmdLine, cr, + "Got bad perfdata value " + JsonEncode(perfdata) + " from IfW API on host '" + + psHost + "' port '" + psPort + "', expected an array of strings", + start, end + ); + return; + } + } + } + ReportIfwCheckResult(checkable, cmdLine, cr, result->Get("checkresult"), start, end, exitcode, perfdata); } From dc47ad4e70c9ad4a195fa61875cc7609f583854a Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 21 Jun 2023 15:45:10 +0200 Subject: [PATCH 88/97] Helpful messages --- lib/methods/ifwapichecktask.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 988bac9df3b..4af1645085e 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -185,7 +185,9 @@ static void DoIfwNetIo( if (!jsonRoot.IsObjectType()) { ReportIfwCheckResult( checkable, cmdLine, cr, - "Got JSON, but not an object, from IfW API on host '" + psHost + "' port '" + psPort + "'", start, end + "Got JSON, but not an object, from IfW API on host '" + + psHost + "' port '" + psPort + "': " + JsonEncode(jsonRoot), + start, end ); return; } @@ -195,7 +197,9 @@ static void DoIfwNetIo( if (!Dictionary::Ptr(jsonRoot)->Get(psCommand, &jsonBranch)) { ReportIfwCheckResult( checkable, cmdLine, cr, - "Missing ." + psCommand + " in JSON object from IfW API on host '" + psHost + "' port '" + psPort + "'", start, end + "Missing ." + psCommand + " in JSON object from IfW API on host '" + + psHost + "' port '" + psPort + "': " + JsonEncode(jsonRoot), + start, end ); return; } @@ -203,7 +207,9 @@ static void DoIfwNetIo( if (!jsonBranch.IsObjectType()) { ReportIfwCheckResult( checkable, cmdLine, cr, - "." + psCommand + " is not an object in JSON from IfW API on host '" + psHost + "' port '" + psPort + "'", start, end + "." + psCommand + " in JSON from IfW API on host '" + + psHost + "' port '" + psPort + "' is not an object: " + JsonEncode(jsonBranch), + start, end ); return; } @@ -215,7 +221,8 @@ static void DoIfwNetIo( if (!result->Get("exitcode", &exitcode)) { ReportIfwCheckResult( checkable, cmdLine, cr, - "Missing ." + psCommand + ".exitcode in JSON object from IfW API on host '" + psHost + "' port '" + psPort + "'", + "Missing ." + psCommand + ".exitcode in JSON object from IfW API on host '" + + psHost + "' port '" + psPort + "': " + JsonEncode(result), start, end ); return; From 9ce70c87408130ed91bbb769f45b22917528d2ed Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 21 Jun 2023 14:33:40 +0200 Subject: [PATCH 89/97] #include --- lib/methods/ifwapichecktask.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 4af1645085e..932090a8856 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include From 69c30162684f3b7a759a3205756f4fb9ff97e7fd Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 23 Jun 2023 11:20:52 +0200 Subject: [PATCH 90/97] namespace --- lib/methods/ifwapichecktask.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 932090a8856..f2b7ff01887 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -90,12 +90,10 @@ static void DoIfwNetIo( AsioTlsStream& conn, boost::beast::http::request& req, double start ) { - using namespace boost::asio; - using namespace boost::beast; - using namespace boost::beast::http; + namespace http = boost::beast::http; - flat_buffer buf; - response resp; + boost::beast::flat_buffer buf; + http::response resp; try { Connect(conn.lowest_layer(), psHost, psPort, yc); @@ -141,7 +139,7 @@ static void DoIfwNetIo( } try { - async_write(conn, req, yc); + http::async_write(conn, req, yc); conn.async_flush(yc); } catch (const std::exception& ex) { ReportIfwCheckResult( @@ -153,7 +151,7 @@ static void DoIfwNetIo( } try { - async_read(conn, buf, resp, yc); + http::async_read(conn, buf, resp, yc); } catch (const std::exception& ex) { ReportIfwCheckResult( yc, checkable, cmdLine, cr, From f83bf559bce6415116e7ec31cb6570ae568087a4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 23 Jun 2023 11:39:21 +0200 Subject: [PATCH 91/97] namespace --- lib/methods/ifwapichecktask.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index f2b7ff01887..28f3829c3d1 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -277,9 +277,8 @@ static void DoIfwNetIo( void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { - using namespace boost::asio; - using namespace boost::beast; - using namespace boost::beast::http; + namespace asio = boost::asio; + namespace http = boost::beast::http; REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); @@ -445,14 +444,14 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes static const auto userAgent ("Icinga/" + Application::GetAppVersion()); auto relative (uri->Format()); auto body (JsonEncode(params)); - auto req (Shared>::Make()); + auto req (Shared>::Make()); - req->method(verb::post); + req->method(http::verb::post); req->target(relative); - req->set(field::accept, "application/json"); - req->set(field::content_type, "application/json"); - req->set(field::host, expectedSan + ":" + psPort); - req->set(field::user_agent, userAgent); + req->set(http::field::accept, "application/json"); + req->set(http::field::content_type, "application/json"); + req->set(http::field::host, expectedSan + ":" + psPort); + req->set(http::field::user_agent, userAgent); req->body() = body; req->content_length(req->body().size()); @@ -481,14 +480,14 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (!username.IsEmpty() && !password.IsEmpty()) { auto authn (username + ":" + password); - req->set(field::authorization, "Basic " + Base64::Encode(authn)); + req->set(http::field::authorization, "Basic " + Base64::Encode(authn)); cmdLine->Add("--user"); cmdLine->Add(authn); } auto& io (IoEngine::Get().GetIoContext()); - auto strand (Shared::Make(io)); - Shared::Ptr ctx; + auto strand (Shared::Make(io)); + Shared::Ptr ctx; double start = Utility::GetTime(); try { @@ -502,7 +501,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes IoEngine::SpawnCoroutine( *strand, - [strand, checkable, cmdLine, cr, psCommand, psHost, expectedSan, psPort, conn, req, start, checkTimeout](boost::asio::yield_context yc) { + [strand, checkable, cmdLine, cr, psCommand, psHost, expectedSan, psPort, conn, req, start, checkTimeout](asio::yield_context yc) { Timeout::Ptr timeout = new Timeout(strand->context(), *strand, boost::posix_time::microseconds(int64_t(checkTimeout * 1e6)), [&conn, &checkable](boost::asio::yield_context yc) { Log(LogNotice, "IfwApiCheckTask") From bf14f4995d9ef6235a7fee3895bf1a7965fe264f Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 23 Jun 2023 13:22:05 +0200 Subject: [PATCH 92/97] Re: https://github.com/Icinga/icinga2/pull/9062#pullrequestreview-1307534589 --- lib/methods/ifwapichecktask.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 28f3829c3d1..544cf427e65 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -68,9 +68,7 @@ static void ReportIfwCheckResult( double end = Utility::GetTime(); CpuBoundWork cbw (yc); - Utility::QueueAsyncCallback([checkable, cmdLine, cr, output, start, end]() { - ReportIfwCheckResult(checkable, cmdLine, cr, output, start, end); - }); + ReportIfwCheckResult(checkable, cmdLine, cr, output, start, end); } static const char* GetUnderstandableError(const std::exception& ex) From ac74d6e17fbb2eb58d9c7a61b43913ab09d1966f Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 23 Jun 2023 16:26:47 +0200 Subject: [PATCH 93/97] namespace --- lib/methods/ifwapichecktask.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 544cf427e65..23f65a7a809 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -277,6 +277,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes { namespace asio = boost::asio; namespace http = boost::beast::http; + using http::field; REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); @@ -446,10 +447,10 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes req->method(http::verb::post); req->target(relative); - req->set(http::field::accept, "application/json"); - req->set(http::field::content_type, "application/json"); - req->set(http::field::host, expectedSan + ":" + psPort); - req->set(http::field::user_agent, userAgent); + req->set(field::accept, "application/json"); + req->set(field::content_type, "application/json"); + req->set(field::host, expectedSan + ":" + psPort); + req->set(field::user_agent, userAgent); req->body() = body; req->content_length(req->body().size()); @@ -478,7 +479,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (!username.IsEmpty() && !password.IsEmpty()) { auto authn (username + ":" + password); - req->set(http::field::authorization, "Basic " + Base64::Encode(authn)); + req->set(field::authorization, "Basic " + Base64::Encode(authn)); cmdLine->Add("--user"); cmdLine->Add(authn); } From 44e1a75d93a994b799a53fee3868e338d5d65377 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 30 Jun 2023 13:36:10 +0200 Subject: [PATCH 94/97] Check multi :( --- lib/methods/ifwapichecktask.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 23f65a7a809..3cf6b518219 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -48,6 +48,19 @@ static void ReportIfwCheckResult( Checkable::ExecuteCommandProcessFinishedHandler(cmdLine, pr); } else { + if (perfdata) { + Array::Ptr splittedPerfdata = new Array(); + + { + ObjectLock oLock (perfdata); + for (String pv : perfdata) { + PluginUtility::SplitPerfdata(pv)->CopyTo(splittedPerfdata); + } + } + + perfdata = splittedPerfdata; + } + cr->SetOutput(output); cr->SetPerformanceData(perfdata); cr->SetState((ServiceState)exitcode); From 10ffcb8189521121af186890deb8dbf5240a6bd8 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 30 Jun 2023 14:47:54 +0200 Subject: [PATCH 95/97] build fix --- lib/methods/ifwapichecktask.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 3cf6b518219..576b95047d4 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -48,21 +48,19 @@ static void ReportIfwCheckResult( Checkable::ExecuteCommandProcessFinishedHandler(cmdLine, pr); } else { + auto splittedPerfdata (perfdata); + if (perfdata) { - Array::Ptr splittedPerfdata = new Array(); + splittedPerfdata = new Array(); + ObjectLock oLock (perfdata); - { - ObjectLock oLock (perfdata); - for (String pv : perfdata) { - PluginUtility::SplitPerfdata(pv)->CopyTo(splittedPerfdata); - } + for (String pv : perfdata) { + PluginUtility::SplitPerfdata(pv)->CopyTo(splittedPerfdata); } - - perfdata = splittedPerfdata; } cr->SetOutput(output); - cr->SetPerformanceData(perfdata); + cr->SetPerformanceData(splittedPerfdata); cr->SetState((ServiceState)exitcode); cr->SetExitStatus(exitcode); cr->SetExecutionStart(start); From a994c646ef0b3a301d1b8a4ed04d146a11039061 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 5 Jul 2023 17:29:30 +0200 Subject: [PATCH 96/97] curl --fail-with-body --- lib/methods/ifwapichecktask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp index 576b95047d4..8516d70c033 100644 --- a/lib/methods/ifwapichecktask.cpp +++ b/lib/methods/ifwapichecktask.cpp @@ -468,7 +468,7 @@ void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes static const auto curlTlsMinVersion ((String("--") + DEFAULT_TLS_PROTOCOLMIN).ToLower()); Array::Ptr cmdLine = new Array({ - "curl", "--verbose", curlTlsMinVersion, "--fail", + "curl", "--verbose", curlTlsMinVersion, "--fail-with-body", "--connect-to", expectedSan + ":" + psPort + ":" + psHost + ":" + psPort, "--ciphers", DEFAULT_TLS_CIPHERS, "--cert", cert, From 0d72f553ed94fd9739ec33fe54db124b4ff4db5a Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 6 Jul 2023 10:39:56 +0200 Subject: [PATCH 97/97] Shorten docs, refer to https://icinga.com/docs/icinga-for-windows/latest/doc/110-Installation/30-API-Check-Forwarder/ --- doc/10-icinga-template-library.md | 44 +++++++------------------------ 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 8bad81b27e5..ca06837c881 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -191,39 +191,15 @@ sleep\_time | **Optional.** The duration of the sleep in seconds. Defaults t Built-in check command for executing arbitrary PowerShell check commands via the [Icinga for Windows REST API](https://icinga.com/docs/icinga-for-windows/latest/doc/110-Installation/30-API-Check-Forwarder/). +Consult that documentation for why and how to optimally use the `ifw-api` +command as an addon for existing Icinga clusters with Icinga for Windows. -That feature lets the PowerShell processes spawned by Icinga just talk -to the pre-loaded IfW API instead of loading all PowerShell check commands -by itself on every check. In contrast the `ifw-api` command doesn't even spawn +In short, that feature lets the PowerShell processes spawned by Icinga just +talk to the pre-loaded IfW API instead of loading all PowerShell check commands +by itself on every check. In contrast, the `ifw-api` command doesn't even spawn any process, but communicates directly with the IfW API. -Ideally you run the Icinga Director and have imported our PowerShell check commands from -[their basket](https://icinga.com/docs/icinga-for-windows/latest/doc/200-Icinga-Integration/01-Director-Baskets/). -In this case adding `ifw-api` to the imports of the _PowerShell Base_ -check command is enough to enable `ifw-api` globally. - -!!! warning - - Do the latter only if your **entire** Icinga cluster runs v2.14+! Otherwise, - all older nodes which load the modified _PowerShell Base_ (e.g. from a global - zone) will reject all new configuration as the imported `ifw-api` is missing. - -For a cluster which may contain older Icinga instances there's a workaround: -Put the following command in a global zone and import it instead of `ifw-api`: - -``` -object CheckCommand "ifw-api-if-exists" { - try { - import "ifw-api" - } except { - } -} -``` - -In any case re-running the Kickstart Wizard is required -for the Director to recognise these new check commands. - -The `ifw-api` command may be also used like e.g. [check_by_ssh](#plugin-check-command-by-ssh). +It may be also used like e.g. [check_by_ssh](#plugin-check-command-by-ssh). Its custom variables provide high flexibility. From using a custom CA to controlling the IfW API directly from a Linux satellite. @@ -250,18 +226,16 @@ Optional custom variables passed as [command parameters](03-monitoring-basics.md contain functions for the case `ifw-api` is used with command endpoints. Only macro strings referring to custom variables which are set to functions work. -The above defaults allow enabling `ifw-api(-if-exists)` globally by importing -it into _PowerShell Base_ without additional configuration elsewhere: +#### Remarks * `$command.name$` is resolved at runtime to the name of the specific check command being run and not any of the templates it imports, i.e. it - becomes e.g. "Invoke-IcingaCheckCPU", not "PowerShell Base" or even "ifw-api" -* Our PowerShell framework provides ifw\_api\_arguments for all of its commands. + becomes e.g. "Invoke-IcingaCheckCPU" if "ifw-api" is imported there * `ifw-api` connects to localhost (if ifw\_api\_host is null), but expects the peer to identify itself via TLS with the NodeName of the endpoint actually running the command (if ifw\_api\_expected\_san is null) * The actual values of ifw\_api\_cert, ifw\_api\_key, ifw\_api\_ca and ifw\_api\_crl - are also resolved on the command endpoint if null + are also resolved to the Icinga PKI on the command endpoint if null