[pkg-nagios-changes] [Git][nagios-team/pkg-icinga2][master] 4 commits: New upstream version 2.12.2
Bas Couwenberg
gitlab at salsa.debian.org
Thu Nov 26 05:00:48 GMT 2020
Bas Couwenberg pushed to branch master at Debian Nagios Maintainer Group / pkg-icinga2
Commits:
f3aa2f9d by Bas Couwenberg at 2020-11-26T05:30:54+01:00
New upstream version 2.12.2
- - - - -
1367bea8 by Bas Couwenberg at 2020-11-26T05:31:22+01:00
Update upstream source from tag 'upstream/2.12.2'
Update to upstream version '2.12.2'
with Debian dir ba52329eb85a526e903129ee66d3bd67115dd5a3
- - - - -
1b3d6223 by Bas Couwenberg at 2020-11-26T05:31:47+01:00
New upstream release.
- - - - -
3c21532f by Bas Couwenberg at 2020-11-26T05:33:02+01:00
Set distribution to unstable.
- - - - -
10 changed files:
- .github/workflows/packages.yml
- AUTHORS
- CHANGELOG.md
- VERSION
- debian/changelog
- lib/remote/apilistener-configsync.cpp
- lib/remote/apilistener-filesync.cpp
- lib/remote/apilistener.cpp
- lib/remote/httpserverconnection.cpp
- lib/remote/jsonrpcconnection.cpp
Changes:
=====================================
.github/workflows/packages.yml
=====================================
@@ -21,6 +21,9 @@ jobs:
- name: debian
codename: stretch
has32bit: true
+ - name: ubuntu
+ codename: groovy
+ has32bit: false
- name: ubuntu
codename: focal
has32bit: false
=====================================
AUTHORS
=====================================
@@ -75,7 +75,7 @@ Elias Ohm <eohm at novomind.com>
Eric Lippmann <eric.lippmann at icinga.com>
Evgeni Golov <evgeni at golov.de>
Ewoud Kohl van Wijngaarden <ewoud at kohlvanwijngaarden.nl>
-Fabian Röhl <FRoehl at freicon.de>
+Fabian Röhl <mail at fabian-roehl.de>
fbachmann <bachmann.f at gmail.com>
Federico Cuello <federico.cuello at sociomantic.com>
Federico Pires <federico.pires at upsight.com>
@@ -130,7 +130,7 @@ krishna <gskrishna44 at gmail.com>
Lars Engels <lars.engels at 0x20.net>
Lars Krüger <krueger-lars at web.de>
Leah Oswald <mail at leahoswald.de>
-Lee Clemens <java at leeclemens.net>
+Lee Clemens <sftw at leeclemens.net>
Lee Garrett <lgarrett at rocketjump.eu>
Lennart Betz <lennart.betz at icinga.com>
Leon Stringer <leon at priorsvle.com>
=====================================
CHANGELOG.md
=====================================
@@ -7,6 +7,23 @@ documentation before upgrading to a new release.
Released closed milestones can be found on [GitHub](https://github.com/Icinga/icinga2/milestones?state=closed).
+## 2.12.2 (2020-12-01)
+
+Version 2.12.2 fixes several issues to improve the reliability of the cluster functionality.
+
+### Bugfixes
+
+* Fix a connection leak with misconfigured agents #8483
+* Properly sync changes of config objects in global zones done via the API #8474 #8470
+* Prevent other clients from being disconnected when replaying the cluster log takes very long #8496
+* Avoid duplicate connections between endpoints #8465
+* Ignore incoming config object updates for unknown zones #8461
+* Check timestamps before removing files in config sync #8495
+
+### Enhancements
+
+* Include HTTP status codes in log #8467
+
## 2.12.1 (2020-10-15)
Version 2.12.1 fixes several crashes, deadlocks and excessive check latencies.
=====================================
VERSION
=====================================
@@ -1,2 +1,2 @@
-Version: 2.12.1
+Version: 2.12.2
Revision: 1
=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+icinga2 (2.12.2-1) unstable; urgency=medium
+
+ * Team upload.
+ * New upstream release.
+
+ -- Bas Couwenberg <sebastic at debian.org> Thu, 26 Nov 2020 05:32:44 +0100
+
icinga2 (2.12.1-2) unstable; urgency=medium
* Team upload.
=====================================
lib/remote/apilistener-configsync.cpp
=====================================
@@ -72,6 +72,16 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin
return Empty;
}
+ String objZone = params->Get("zone");
+
+ if (!Zone::GetByName(objZone)) {
+ Log(LogNotice, "ApiListener")
+ << "Discarding 'config update object' message"
+ << " from '" << identity << "' (endpoint: '" << endpoint->GetName() << "', zone: '" << endpointZone->GetName() << "')"
+ << " for object '" << objName << "' of type '" << objType << "'. Objects zone '" << objZone << "' isn't known locally.";
+ return Empty;
+ }
+
/* ignore messages if the endpoint does not accept config */
if (!listener->GetAcceptConfig()) {
Log(LogWarning, "ApiListener")
@@ -315,6 +325,7 @@ void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const Mess
params->Set("name", object->GetName());
params->Set("type", object->GetReflectionType()->GetName());
params->Set("version", object->GetVersion());
+ params->Set("zone", object->GetZoneName());
if (object->GetPackage() == "_api") {
String file;
@@ -423,7 +434,7 @@ void ApiListener::DeleteConfigObject(const ConfigObject::Ptr& object, const Mess
if (!target)
target = Zone::GetLocalZone();
- RelayMessage(origin, target, message, false);
+ RelayMessage(origin, target, message, true);
}
}
=====================================
lib/remote/apilistener-filesync.cpp
=====================================
@@ -421,6 +421,12 @@ void ApiListener::HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dic
Dictionary::Ptr productionConfig = MergeConfigUpdate(productionConfigInfo);
Dictionary::Ptr newConfig = MergeConfigUpdate(newConfigInfo);
+ bool timestampChanged = false;
+
+ if (CompareTimestampsConfigChange(productionConfig, newConfig, stageConfigZoneDir)) {
+ timestampChanged = true;
+ }
+
/* If we have received 'checksums' via cluster message, go for it.
* Otherwise do the old timestamp dance for versions < 2.11.
*/
@@ -429,7 +435,7 @@ void ApiListener::HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dic
<< "Received configuration for zone '" << zoneName << "' from endpoint '"
<< fromEndpointName << "'. Comparing the timestamp and checksums.";
- if (CompareTimestampsConfigChange(productionConfig, newConfig, stageConfigZoneDir)) {
+ if (timestampChanged) {
if (CheckConfigChange(productionConfigInfo, newConfigInfo))
configChange = true;
@@ -446,7 +452,7 @@ void ApiListener::HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dic
<< "Received configuration update without checksums from parent endpoint "
<< fromEndpointName << ". This behaviour is deprecated. Please upgrade the parent endpoint to 2.11+";
- if (CompareTimestampsConfigChange(productionConfig, newConfig, stageConfigZoneDir)) {
+ if (timestampChanged) {
configChange = true;
}
@@ -508,8 +514,8 @@ void ApiListener::HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dic
<< "Applying configuration file update for path '" << stageConfigZoneDir << "' ("
<< numBytes << " Bytes).";
- // If the update removes a path, delete it on disk and signal a config change.
- {
+ if (timestampChanged) {
+ // If the update removes a path, delete it on disk and signal a config change.
ObjectLock xlock(productionConfig);
for (const Dictionary::Pair& kv : productionConfig) {
=====================================
lib/remote/apilistener.cpp
=====================================
@@ -679,19 +679,21 @@ void ApiListener::NewClientHandlerInternal(
if (ctype == ClientJsonRpc) {
Log(LogNotice, "ApiListener", "New JSON-RPC client");
+ if (endpoint && endpoint->GetConnected()) {
+ Log(LogNotice, "ApiListener")
+ << "Ignoring JSON-RPC connection " << conninfo
+ << ". We're already connected to Endpoint '" << endpoint->GetName() << "'.";
+ return;
+ }
+
JsonRpcConnection::Ptr aclient = new JsonRpcConnection(identity, verify_ok, client, role);
if (endpoint) {
- bool needSync = !endpoint->GetConnected();
-
endpoint->AddClient(aclient);
- IoEngine::SpawnCoroutine(IoEngine::Get().GetIoContext(), [this, aclient, endpoint, needSync](asio::yield_context yc) {
- CpuBoundWork syncClient (yc);
-
- SyncClient(aclient, endpoint, needSync);
+ Utility::QueueAsyncCallback([this, aclient, endpoint]() {
+ SyncClient(aclient, endpoint, true);
});
-
} else if (!AddAnonymousClient(aclient)) {
Log(LogNotice, "ApiListener")
<< "Ignoring anonymous JSON-RPC connection " << conninfo
@@ -1032,6 +1034,17 @@ void ApiListener::SyncSendMessage(const Endpoint::Ptr& endpoint, const Dictionar
}
}
+/**
+ * Relay a message to a directly connected zone or to a global zone.
+ * If some other zone is passed as the target zone, it is not relayed.
+ *
+ * @param targetZone The zone to relay to
+ * @param origin Information about where this message is relayed from (if it was not generated locally)
+ * @param message The message to relay
+ * @param currentZoneMaster The current master node of the local zone
+ * @return true if the message has been relayed to all relevant endpoints,
+ * false if it hasn't and must be persisted in the replay log
+ */
bool ApiListener::RelayMessageOne(const Zone::Ptr& targetZone, const MessageOrigin::Ptr& origin, const Dictionary::Ptr& message, const Endpoint::Ptr& currentZoneMaster)
{
ASSERT(targetZone);
@@ -1050,82 +1063,88 @@ bool ApiListener::RelayMessageOne(const Zone::Ptr& targetZone, const MessageOrig
std::vector<Endpoint::Ptr> skippedEndpoints;
- bool relayed = false, log_needed = false, log_done = false;
-
- std::set<Endpoint::Ptr> targetEndpoints;
-
+ std::set<Zone::Ptr> allTargetZones;
if (targetZone->GetGlobal()) {
- targetEndpoints = localZone->GetEndpoints();
-
+ /* if the zone is global, the message has to be relayed to our local zone and direct children */
+ allTargetZones.insert(localZone);
for (const Zone::Ptr& zone : ConfigType::GetObjectsByType<Zone>()) {
- /* Fetch immediate child zone members */
if (zone->GetParent() == localZone) {
- std::set<Endpoint::Ptr> endpoints = zone->GetEndpoints();
- targetEndpoints.insert(endpoints.begin(), endpoints.end());
+ allTargetZones.insert(zone);
}
}
} else {
- targetEndpoints = targetZone->GetEndpoints();
+ /* whereas if it's not global, the message is just relayed to the zone itself */
+ allTargetZones.insert(targetZone);
}
- for (const Endpoint::Ptr& targetEndpoint : targetEndpoints) {
- /* Don't relay messages to ourselves. */
- if (targetEndpoint == localEndpoint)
- continue;
+ bool needsReplay = false;
- log_needed = true;
+ for (const Zone::Ptr& currentTargetZone : allTargetZones) {
+ bool relayed = false, log_needed = false, log_done = false;
- /* Don't relay messages to disconnected endpoints. */
- if (!targetEndpoint->GetConnected()) {
- if (targetZone == localZone)
- log_done = false;
+ for (const Endpoint::Ptr& targetEndpoint : currentTargetZone->GetEndpoints()) {
+ /* Don't relay messages to ourselves. */
+ if (targetEndpoint == localEndpoint)
+ continue;
- continue;
- }
+ log_needed = true;
- log_done = true;
+ /* Don't relay messages to disconnected endpoints. */
+ if (!targetEndpoint->GetConnected()) {
+ if (currentTargetZone == localZone)
+ log_done = false;
- /* Don't relay the message to the zone through more than one endpoint unless this is our own zone.
- * 'relayed' is set to true on success below, enabling the checks in the second iteration.
- */
- if (relayed && targetZone != localZone) {
- skippedEndpoints.push_back(targetEndpoint);
- continue;
- }
+ continue;
+ }
- /* Don't relay messages back to the endpoint which we got the message from. */
- if (origin && origin->FromClient && targetEndpoint == origin->FromClient->GetEndpoint()) {
- skippedEndpoints.push_back(targetEndpoint);
- continue;
- }
+ log_done = true;
- /* Don't relay messages back to the zone which we got the message from. */
- if (origin && origin->FromZone && targetZone == origin->FromZone) {
- skippedEndpoints.push_back(targetEndpoint);
- continue;
- }
+ /* Don't relay the message to the zone through more than one endpoint unless this is our own zone.
+ * 'relayed' is set to true on success below, enabling the checks in the second iteration.
+ */
+ if (relayed && currentTargetZone != localZone) {
+ skippedEndpoints.push_back(targetEndpoint);
+ continue;
+ }
- /* Only relay message to the zone master if we're not currently the zone master.
- * e1 is zone master, e2 and e3 are zone members.
- *
- * Message is sent from e2 or e3:
- * !isMaster == true
- * targetEndpoint e1 is zone master -> send the message
- * targetEndpoint e3 is not zone master -> skip it, avoid routing loops
- *
- * Message is sent from e1:
- * !isMaster == false -> send the messages to e2 and e3 being the zone routing master.
- */
- bool isMaster = (currentZoneMaster == localEndpoint);
+ /* Don't relay messages back to the endpoint which we got the message from. */
+ if (origin && origin->FromClient && targetEndpoint == origin->FromClient->GetEndpoint()) {
+ skippedEndpoints.push_back(targetEndpoint);
+ continue;
+ }
- if (!isMaster && targetEndpoint != currentZoneMaster) {
- skippedEndpoints.push_back(targetEndpoint);
- continue;
- }
+ /* Don't relay messages back to the zone which we got the message from. */
+ if (origin && origin->FromZone && currentTargetZone == origin->FromZone) {
+ skippedEndpoints.push_back(targetEndpoint);
+ continue;
+ }
+
+ /* Only relay message to the zone master if we're not currently the zone master.
+ * e1 is zone master, e2 and e3 are zone members.
+ *
+ * Message is sent from e2 or e3:
+ * !isMaster == true
+ * targetEndpoint e1 is zone master -> send the message
+ * targetEndpoint e3 is not zone master -> skip it, avoid routing loops
+ *
+ * Message is sent from e1:
+ * !isMaster == false -> send the messages to e2 and e3 being the zone routing master.
+ */
+ bool isMaster = (currentZoneMaster == localEndpoint);
+
+ if (!isMaster && targetEndpoint != currentZoneMaster) {
+ skippedEndpoints.push_back(targetEndpoint);
+ continue;
+ }
- relayed = true;
+ relayed = true;
- SyncSendMessage(targetEndpoint, message);
+ SyncSendMessage(targetEndpoint, message);
+ }
+
+ if (log_needed && !log_done) {
+ needsReplay = true;
+ }
}
if (!skippedEndpoints.empty()) {
@@ -1135,7 +1154,7 @@ bool ApiListener::RelayMessageOne(const Zone::Ptr& targetZone, const MessageOrig
skippedEndpoint->SetLocalLogPosition(ts);
}
- return !log_needed || log_done;
+ return !needsReplay;
}
void ApiListener::SyncRelayMessage(const MessageOrigin::Ptr& origin,
=====================================
lib/remote/httpserverconnection.cpp
=====================================
@@ -539,12 +539,16 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc)
authenticatedUser = ApiUser::GetByAuthHeader(request[http::field::authorization].to_string());
}
- Log(LogInformation, "HttpServerConnection")
- << "Request: " << request.method_string() << ' ' << request.target()
+ Log logMsg (LogInformation, "HttpServerConnection");
+
+ logMsg << "Request: " << request.method_string() << ' ' << request.target()
<< " (from " << m_PeerAddress
<< "), user: " << (authenticatedUser ? authenticatedUser->GetName() : "<unauthenticated>")
- << ", agent: " << request[http::field::user_agent] << ")."; //operator[] - Returns the value for a field, or "" if it does not exist.
+ << ", agent: " << request[http::field::user_agent]; //operator[] - Returns the value for a field, or "" if it does not exist.
+ Defer addRespCode ([&response, &logMsg]() {
+ logMsg << ", status: " << response.result() << ").";
+ });
if (!HandleAccessControl(*m_Stream, request, response, yc)) {
break;
=====================================
lib/remote/jsonrpcconnection.cpp
=====================================
@@ -348,20 +348,43 @@ void JsonRpcConnection::CheckLiveness(boost::asio::yield_context yc)
{
boost::system::error_code ec;
- for (;;) {
- m_CheckLivenessTimer.expires_from_now(boost::posix_time::seconds(30));
+ if (!m_Authenticated) {
+ /* Anonymous connections are normally only used for requesting a certificate and are closed after this request
+ * is received. However, the request is only sent if the child has successfully verified the certificate of its
+ * parent so that it is an authenticated connection from its perspective. In case this verification fails, both
+ * ends view it as an anonymous connection and never actually use it but attempt a reconnect after 10 seconds
+ * leaking the connection. Therefore close it after a timeout.
+ */
+
+ m_CheckLivenessTimer.expires_from_now(boost::posix_time::seconds(10));
m_CheckLivenessTimer.async_wait(yc[ec]);
if (m_ShuttingDown) {
- break;
+ return;
}
- if (m_Seen < Utility::GetTime() - 60 && (!m_Endpoint || !m_Endpoint->GetSyncing())) {
- Log(LogInformation, "JsonRpcConnection")
- << "No messages for identity '" << m_Identity << "' have been received in the last 60 seconds.";
+ auto remote (m_Stream->lowest_layer().remote_endpoint());
- Disconnect();
- break;
+ Log(LogInformation, "JsonRpcConnection")
+ << "Closing anonymous connection [" << remote.address() << "]:" << remote.port() << " after 10 seconds.";
+
+ Disconnect();
+ } else {
+ for (;;) {
+ m_CheckLivenessTimer.expires_from_now(boost::posix_time::seconds(30));
+ m_CheckLivenessTimer.async_wait(yc[ec]);
+
+ if (m_ShuttingDown) {
+ break;
+ }
+
+ if (m_Seen < Utility::GetTime() - 60 && (!m_Endpoint || !m_Endpoint->GetSyncing())) {
+ Log(LogInformation, "JsonRpcConnection")
+ << "No messages for identity '" << m_Identity << "' have been received in the last 60 seconds.";
+
+ Disconnect();
+ break;
+ }
}
}
}
View it on GitLab: https://salsa.debian.org/nagios-team/pkg-icinga2/-/compare/3bc257cbb19ce27799bcdac2d93c8e7510ee660f...3c21532fdb340248aef25d626c911ee4bd20b9d3
--
View it on GitLab: https://salsa.debian.org/nagios-team/pkg-icinga2/-/compare/3bc257cbb19ce27799bcdac2d93c8e7510ee660f...3c21532fdb340248aef25d626c911ee4bd20b9d3
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-nagios-changes/attachments/20201126/51b9c48d/attachment-0001.html>
More information about the pkg-nagios-changes
mailing list