]> granicus.if.org Git - icinga2/commitdiff
ElasticWriter: Implement support for TLS connections (HTTP proxy)
authorMichael Friedrich <michael.friedrich@icinga.com>
Mon, 11 Sep 2017 15:28:41 +0000 (17:28 +0200)
committerGunnar Beutner <gunnar.beutner@icinga.com>
Tue, 12 Sep 2017 10:52:49 +0000 (12:52 +0200)
This commit also enhances the log messages.

refs #5538

doc/09-object-types.md
doc/14-features.md
lib/perfdata/elasticwriter.cpp
lib/perfdata/elasticwriter.ti

index 5c11b4af9dbd6b7e64d402ec1bda51272799c16b..e278baaee0b5ca245d59e2ccf3547d5afb118781 100644 (file)
@@ -997,15 +997,25 @@ Configuration Attributes:
   host                   | **Required.** Elasticsearch host address. Defaults to `127.0.0.1`.
   port                   | **Required.** Elasticsearch port. Defaults to `9200`.
   index                  | **Required.** Elasticsearch index name. Defaults to `icinga2`.
-  enable_send_perfdata   | **Optional.** Send parsed performance data metrics for check results. Defaults to `false`.
-  flush_interval         | **Optional.** How long to buffer data points before transfering to Elasticsearch. Defaults to `10`.
-  flush_threshold        | **Optional.** How many data points to buffer before forcing a transfer to Elasticsearch.  Defaults to `1024`.
+  enable\_send\_perfdata | **Optional.** Send parsed performance data metrics for check results. Defaults to `false`.
+  flush\_interval        | **Optional.** How long to buffer data points before transfering to Elasticsearch. Defaults to `10`.
+  flush\_threshold       | **Optional.** How many data points to buffer before forcing a transfer to Elasticsearch.  Defaults to `1024`.
   username               | **Optional.** Basic auth username if Elasticsearch is hidden behind an HTTP proxy.
   password               | **Optional.** Basic auth password if Elasticsearch is hidden behind an HTTP proxy.
+  enable\_tls            | **Optional.** Whether to use a TLS stream. Defaults to `false`. Requires an HTTP proxy.
+  ca\_path               | **Optional.** CA certificate to validate the remote host. Requires `enable_tls` set to `true`.
+  cert\_path             | **Optional.** Host certificate to present to the remote host for mutual verification. Requires `enable_tls` set to `true`.
+  key\_path              | **Optional.** Host key to accompany the cert\_path. Requires `enable_tls` set to `true`.
 
 Note: If `flush_threshold` is set too low, this will force the feature to flush all data to Elasticsearch too often.
 Experiment with the setting, if you are processing more than 1024 metrics per second or similar.
 
+Basic auth is supported with the `username` and `password` attributes. This requires an
+HTTP proxy (Nginx, etc.) in front of the Elasticsearch instance.
+
+TLS for the HTTP proxy can be enabled with `enable_tls`. In addition to that
+you can specify the certificates with the `ca_path`, `cert_path` and `cert_key` attributes.
+
 ## LiveStatusListener <a id="objecttype-livestatuslistener"></a>
 
 Livestatus API interface available as TCP or UNIX socket. Historical table queries
index 70c75098ee746cca8b22f3ff2b751e108dca8845..5b7b5e3727028a1dbc2414c5604d0430c8f6350b 100644 (file)
@@ -288,7 +288,9 @@ The check results include parsed performance data metrics if enabled.
 
 Enable the feature and restart Icinga 2.
 
-    # icinga2 feature enable elastic
+```
+# icinga2 feature enable elastic
+```
 
 The default configuration expects an Elasticsearch instance running on `localhost` on port `9200
  and writes to an index called `icinga2`.
index c8f50aba67817f7c6fd8bb764467573a44fa531f..83278bea7222829c712d6334f9574136f48140e1 100644 (file)
@@ -356,12 +356,13 @@ void ElasticWriter::Enqueue(String type, const Dictionary::Ptr& fields, double t
         * We do it this way to avoid problems with a near full queue.
         */
 
-       String data;
+       String indexBody = "{ \"index\" : { \"_type\" : \"" + eventType + "\" } }\n";
+       String fieldsBody = JsonEncode(fields);
 
-       data += "{ \"index\" : { \"_type\" : \"" + eventType + "\" } }\n";
-       data += JsonEncode(fields);
+       Log(LogDebug, "ElasticWriter")
+           << "Add to fields to message list: '" << fieldsBody << "'.";
 
-       m_DataBuffer.push_back(data);
+       m_DataBuffer.push_back(indexBody + fieldsBody);
 
        /* Flush if we've buffered too much to prevent excessive memory use. */
        if (static_cast<int>(m_DataBuffer.size()) >= GetFlushThreshold()) {
@@ -400,7 +401,8 @@ void ElasticWriter::Flush(void)
 void ElasticWriter::SendRequest(const String& body)
 {
        Url::Ptr url = new Url();
-       url->SetScheme("http");
+
+       url->SetScheme(GetEnableTls() ? "https" : "http");
        url->SetHost(GetHost());
        url->SetPort(GetPort());
 
@@ -433,10 +435,10 @@ void ElasticWriter::SendRequest(const String& body)
        req.RequestMethod = "POST";
        req.RequestUrl = url;
 
-#ifdef I2_DEBUG /* I2_DEBUG */
+       /* Don't log the request body to debug log, this is already done above. */
        Log(LogDebug, "ElasticWriter")
-           << "Sending request" << ((!username.IsEmpty() && !password.IsEmpty()) ? " with basic auth " : "" )  << body;
-#endif /* I2_DEBUG */
+           << "Sending " << req.RequestMethod << " request" << ((!username.IsEmpty() && !password.IsEmpty()) ? " with basic auth" : "" )
+           << " to '" << url->Format() << "'.";
 
        try {
                req.WriteBody(body.CStr(), body.GetLength());
@@ -523,7 +525,32 @@ Stream::Ptr ElasticWriter::Connect(void)
                    << "Can't connect to Elasticsearch on host '" << GetHost() << "' port '" << GetPort() << "'.";
                throw ex;
        }
-       return new NetworkStream(socket);
+
+       if (GetEnableTls()) {
+               boost::shared_ptr<SSL_CTX> sslContext;
+
+               try {
+                       sslContext = MakeSSLContext(GetCertPath(), GetKeyPath(), GetCaPath());
+               } catch (const std::exception& ex) {
+                       Log(LogWarning, "ElasticWriter")
+                           << "Unable to create SSL context.";
+                       throw ex;
+               }
+
+               TlsStream::Ptr tlsStream = new TlsStream(socket, GetHost(), RoleClient, sslContext);
+
+               try {
+                       tlsStream->Handshake();
+               } catch (const std::exception& ex) {
+                       Log(LogWarning, "ElasticWriter")
+                           << "TLS handshake with host '" << GetHost() << "' on port " << GetPort() << " failed.";
+                       throw ex;
+               }
+
+               return tlsStream;
+       } else {
+               return new NetworkStream(socket);
+       }
 }
 
 void ElasticWriter::AssertOnWorkQueue(void)
index 023d9e349e621bc14a2f40dbfa7544de9ab1eb8e..cbd8ff17037e5d6d213430c02ea50c836e6cfada 100644 (file)
@@ -22,6 +22,13 @@ class ElasticWriter : ConfigObject
        [config] String username;
        [config] String password;
 
+       [config] bool enable_tls {
+               default {{{ return false; }}}
+       };
+       [config] String ca_path;
+       [config] String cert_path;
+       [config] String key_path;
+
        [config] int flush_interval {
                default {{{ return 10; }}}
        };