Skip to content

Commit

Permalink
Merge pull request #6813 from Icinga/feature/gelfwriter-tls-support
Browse files Browse the repository at this point in the history
 Implement TLS support for the GelfWriter feature
  • Loading branch information
Michael Friedrich authored May 24, 2019
2 parents 2ba2134 + bc0ab93 commit 5d0af5c
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 16 deletions.
5 changes: 4 additions & 1 deletion doc/09-object-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -1344,7 +1344,10 @@ Configuration Attributes:
source | String | **Optional.** Source name for this instance. Defaults to `icinga2`.
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\_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>

Expand Down
74 changes: 60 additions & 14 deletions lib/perfdata/gelfwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
#include "base/statsfunction.hpp"
#include <boost/algorithm/string/replace.hpp>
#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;

Expand Down Expand Up @@ -126,11 +131,7 @@ void GelfWriter::ExceptionHandler(boost::exception_ptr exp)
Log(LogDebug, "GelfWriter")
<< "Exception during Graylog Gelf operation: " << DiagnosticInformation(std::move(exp));

if (GetConnected()) {
m_Stream->Close();

SetConnected(false);
}
DisconnectInternal();
}

void GelfWriter::Reconnect()
Expand All @@ -156,20 +157,46 @@ void GelfWriter::ReconnectInternal()
if (GetConnected())
return;

TcpSocket::Ptr socket = new TcpSocket();

Log(LogNotice, "GelfWriter")
<< "Reconnecting to Graylog Gelf on host '" << GetHost() << "' port '" << GetPort() << "'.";

bool ssl = GetEnableTls();

if (ssl) {
std::shared_ptr<boost::asio::ssl::context> sslContext;

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());
}

try {
socket->Connect(GetHost(), GetPort());
icinga::Connect(ssl ? m_Stream.first->lowest_layer() : m_Stream.second->lowest_layer(), GetHost(), GetPort());
} catch (const std::exception& ex) {
Log(LogCritical, "GelfWriter")
<< "Can't connect to Graylog Gelf on host '" << GetHost() << "' port '" << GetPort() << "'.";
throw ex;
Log(LogWarning, "GelfWriter")
<< "Can't connect to Graylog Gelf on host '" << GetHost() << "' port '" << GetPort() << ".'";
throw;
}

m_Stream = new NetworkStream(socket);
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);

Expand All @@ -194,9 +221,22 @@ void GelfWriter::DisconnectInternal()
if (!GetConnected())
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);

}

void GelfWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
Expand Down Expand Up @@ -456,7 +496,13 @@ void GelfWriter::SendLogMessage(const Checkable::Ptr& checkable, const String& g
Log(LogDebug, "GelfWriter")
<< "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) {
Log(LogCritical, "GelfWriter")
<< "Cannot write to TCP socket on host '" << GetHost() << "' port '" << GetPort() << "'.";
Expand Down
2 changes: 1 addition & 1 deletion lib/perfdata/gelfwriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class GelfWriter final : public ObjectImpl<GelfWriter>
void Pause() override;

private:
Stream::Ptr m_Stream;
OptionalTlsStream m_Stream;
WorkQueue m_WorkQueue{10000000, 1};

Timer::Ptr m_ReconnectTimer;
Expand Down
6 changes: 6 additions & 0 deletions lib/perfdata/gelfwriter.ti
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ class GelfWriter : ConfigObject
[config] bool enable_ha {
default {{{ return false; }}}
};
[config] bool enable_tls {
default {{{ return false; }}}
};
[config] String ca_path;
[config] String cert_path;
[config] String key_path;
};

}

0 comments on commit 5d0af5c

Please sign in to comment.