Merge pull request #6813 from Icinga/feature/gelfwriter-tls-support

Implement TLS support for the GelfWriter feature
This commit is contained in:
Michael Friedrich 2019-05-24 15:50:18 +02:00 committed by GitHub
commit 5d0af5c879
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 18 deletions

View File

@ -1344,7 +1344,10 @@ Configuration Attributes:
source | String | **Optional.** Source name for this instance. Defaults to `icinga2`. source | String | **Optional.** Source name for this instance. Defaults to `icinga2`.
enable\_send\_perfdata | Boolean | **Optional.** Enable performance data for 'CHECK RESULT' events. enable\_send\_perfdata | Boolean | **Optional.** Enable performance data for 'CHECK RESULT' events.
enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-features). Defaults to `false`. enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-features). Defaults to `false`.
enable\_tls | Boolean | **Optional.** Whether to use a TLS stream. Defaults to `false`.
ca\_path | String | **Optional.** Path to CA certificate to validate the remote host. Requires `enable_tls` set to `true`.
cert\_path | String | **Optional.** Path to host certificate to present to the remote host for mutual verification. Requires `enable_tls` set to `true`.
key\_path | String | **Optional.** Path to host key to accompany the cert\_path. Requires `enable_tls` set to `true`.
### GraphiteWriter <a id="objecttype-graphitewriter"></a> ### GraphiteWriter <a id="objecttype-graphitewriter"></a>

View File

@ -22,6 +22,11 @@
#include "base/statsfunction.hpp" #include "base/statsfunction.hpp"
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <utility> #include <utility>
#include "base/io-engine.hpp"
#include <boost/asio/write.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/system/error_code.hpp>
#include <boost/asio/error.hpp>
using namespace icinga; using namespace icinga;
@ -126,11 +131,7 @@ void GelfWriter::ExceptionHandler(boost::exception_ptr exp)
Log(LogDebug, "GelfWriter") Log(LogDebug, "GelfWriter")
<< "Exception during Graylog Gelf operation: " << DiagnosticInformation(std::move(exp)); << "Exception during Graylog Gelf operation: " << DiagnosticInformation(std::move(exp));
if (GetConnected()) { DisconnectInternal();
m_Stream->Close();
SetConnected(false);
}
} }
void GelfWriter::Reconnect() void GelfWriter::Reconnect()
@ -156,20 +157,46 @@ void GelfWriter::ReconnectInternal()
if (GetConnected()) if (GetConnected())
return; return;
TcpSocket::Ptr socket = new TcpSocket();
Log(LogNotice, "GelfWriter") Log(LogNotice, "GelfWriter")
<< "Reconnecting to Graylog Gelf on host '" << GetHost() << "' port '" << GetPort() << "'."; << "Reconnecting to Graylog Gelf on host '" << GetHost() << "' port '" << GetPort() << "'.";
try { bool ssl = GetEnableTls();
socket->Connect(GetHost(), GetPort());
} catch (const std::exception& ex) { if (ssl) {
Log(LogCritical, "GelfWriter") std::shared_ptr<boost::asio::ssl::context> sslContext;
<< "Can't connect to Graylog Gelf on host '" << GetHost() << "' port '" << GetPort() << "'.";
throw ex; try {
sslContext = MakeAsioSslContext(GetCertPath(), GetKeyPath(), GetCaPath());
} catch (const std::exception& ex) {
Log(LogWarning, "GelfWriter")
<< "Unable to create SSL context.";
throw;
}
m_Stream.first = std::make_shared<AsioTlsStream>(IoEngine::Get().GetIoService(), *sslContext, GetHost());
} else {
m_Stream.second = std::make_shared<AsioTcpStream>(IoEngine::Get().GetIoService());
} }
m_Stream = new NetworkStream(socket); try {
icinga::Connect(ssl ? m_Stream.first->lowest_layer() : m_Stream.second->lowest_layer(), GetHost(), GetPort());
} catch (const std::exception& ex) {
Log(LogWarning, "GelfWriter")
<< "Can't connect to Graylog Gelf on host '" << GetHost() << "' port '" << GetPort() << ".'";
throw;
}
if (ssl) {
auto& tlsStream (m_Stream.first->next_layer());
try {
tlsStream.handshake(tlsStream.client);
} catch (const std::exception& ex) {
Log(LogWarning, "GelfWriter")
<< "TLS handshake with host '" << GetHost() << " failed.'";
throw;
}
}
SetConnected(true); SetConnected(true);
@ -194,9 +221,22 @@ void GelfWriter::DisconnectInternal()
if (!GetConnected()) if (!GetConnected())
return; return;
m_Stream->Close(); if (m_Stream.first) {
boost::system::error_code ec;
m_Stream.first->next_layer().shutdown(ec);
// https://stackoverflow.com/a/25703699
// As long as the error code's category is not an SSL category, then the protocol was securely shutdown
if (ec.category() == boost::asio::error::get_ssl_category()) {
Log(LogCritical, "GelfWriter")
<< "TLS shutdown with host '" << GetHost() << "' could not be done securely.";
}
} else if (m_Stream.second) {
m_Stream.second->close();
}
SetConnected(false); SetConnected(false);
} }
void GelfWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) void GelfWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
@ -456,7 +496,13 @@ void GelfWriter::SendLogMessage(const Checkable::Ptr& checkable, const String& g
Log(LogDebug, "GelfWriter") Log(LogDebug, "GelfWriter")
<< "Checkable '" << checkable->GetName() << "' sending message '" << log << "'."; << "Checkable '" << checkable->GetName() << "' sending message '" << log << "'.";
m_Stream->Write(log.CStr(), log.GetLength()); if (m_Stream.first) {
boost::asio::write(*m_Stream.first, boost::asio::buffer(msgbuf.str()));
m_Stream.first->flush();
} else {
boost::asio::write(*m_Stream.second, boost::asio::buffer(msgbuf.str()));
m_Stream.second->flush();
}
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
Log(LogCritical, "GelfWriter") Log(LogCritical, "GelfWriter")
<< "Cannot write to TCP socket on host '" << GetHost() << "' port '" << GetPort() << "'."; << "Cannot write to TCP socket on host '" << GetHost() << "' port '" << GetPort() << "'.";

View File

@ -33,7 +33,7 @@ protected:
void Pause() override; void Pause() override;
private: private:
Stream::Ptr m_Stream; OptionalTlsStream m_Stream;
WorkQueue m_WorkQueue{10000000, 1}; WorkQueue m_WorkQueue{10000000, 1};
Timer::Ptr m_ReconnectTimer; Timer::Ptr m_ReconnectTimer;

View File

@ -31,6 +31,12 @@ class GelfWriter : ConfigObject
[config] bool enable_ha { [config] bool enable_ha {
default {{{ return false; }}} default {{{ return false; }}}
}; };
[config] bool enable_tls {
default {{{ return false; }}}
};
[config] String ca_path;
[config] String cert_path;
[config] String key_path;
}; };
} }