]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
amqp: add tls support 3391/head
authorMärt Bakhoff <mbakhoff@perforce.com>
Fri, 28 Feb 2020 16:31:49 +0000 (18:31 +0200)
committerMärt Bakhoff <mbakhoff@perforce.com>
Sun, 1 Mar 2020 09:21:09 +0000 (11:21 +0200)
src/amqp.c
src/collectd.conf.in
src/collectd.conf.pod

index 481d34e34d0ebaf158b516af066ecc390ffa4943..b2312559e291f707ba0063b1e52d5c72eff0edbf 100644 (file)
@@ -38,6 +38,7 @@
 #include <amqp_framing.h>
 
 #ifdef HAVE_AMQP_TCP_SOCKET_H
+#include <amqp_ssl_socket.h>
 #include <amqp_tcp_socket.h>
 #endif
 #ifdef HAVE_AMQP_SOCKET_H
@@ -75,6 +76,13 @@ struct camqp_config_s {
   char *user;
   char *password;
 
+  bool tls_enabled;
+  bool tls_verify_peer;
+  bool tls_verify_hostname;
+  char *tls_cacert;
+  char *tls_client_cert;
+  char *tls_client_key;
+
   char *exchange;
   char *routing_key;
 
@@ -149,6 +157,9 @@ static void camqp_config_free(void *ptr) /* {{{ */
   sfree(conf->vhost);
   sfree(conf->user);
   sfree(conf->password);
+  sfree(conf->tls_cacert);
+  sfree(conf->tls_client_cert);
+  sfree(conf->tls_client_key);
   sfree(conf->exchange);
   sfree(conf->exchange_type);
   sfree(conf->queue);
@@ -430,20 +441,56 @@ static int camqp_connect(camqp_config_t *conf) /* {{{ */
 #ifdef HAVE_AMQP_TCP_SOCKET
 #define CLOSE_SOCKET() /* amqp_destroy_connection() closes the socket for us   \
                         */
-  /* TODO: add support for SSL using amqp_ssl_socket_new
-   *       and related functions */
-  socket = amqp_tcp_socket_new(conf->connection);
-  if (!socket) {
-    ERROR("amqp plugin: amqp_tcp_socket_new failed.");
-    amqp_destroy_connection(conf->connection);
-    conf->connection = NULL;
-    return ENOMEM;
+
+  if (conf->tls_enabled) {
+    socket = amqp_ssl_socket_new(conf->connection);
+    if (!socket) {
+      ERROR("amqp plugin: amqp_ssl_socket_new failed.");
+      amqp_destroy_connection(conf->connection);
+      conf->connection = NULL;
+      return ENOMEM;
+    }
+
+#if AMQP_VERSION >= 0x00080000
+    amqp_ssl_socket_set_verify_peer(socket, conf->tls_verify_peer);
+    amqp_ssl_socket_set_verify_hostname(socket, conf->tls_verify_hostname);
+#endif
+
+    if (conf->tls_cacert) {
+      status = amqp_ssl_socket_set_cacert(socket, conf->tls_cacert);
+      if (status < 0) {
+        ERROR("amqp plugin: amqp_ssl_socket_set_cacert failed: %s",
+              amqp_error_string2(status));
+        amqp_destroy_connection(conf->connection);
+        conf->connection = NULL;
+        return status;
+      }
+    }
+    if (conf->tls_client_cert && conf->tls_client_key) {
+      status = amqp_ssl_socket_set_key(socket, conf->tls_client_cert,
+                                       conf->tls_client_key);
+      if (status < 0) {
+        ERROR("amqp plugin: amqp_ssl_socket_set_key failed: %s",
+              amqp_error_string2(status));
+        amqp_destroy_connection(conf->connection);
+        conf->connection = NULL;
+        return status;
+      }
+    }
+  } else {
+    socket = amqp_tcp_socket_new(conf->connection);
+    if (!socket) {
+      ERROR("amqp plugin: amqp_tcp_socket_new failed.");
+      amqp_destroy_connection(conf->connection);
+      conf->connection = NULL;
+      return ENOMEM;
+    }
   }
 
   status = amqp_socket_open(socket, CONF(conf, host), conf->port);
   if (status < 0) {
-    status *= -1;
-    ERROR("amqp plugin: amqp_socket_open failed: %s", STRERROR(status));
+    ERROR("amqp plugin: amqp_socket_open failed: %s",
+          amqp_error_string2(status));
     amqp_destroy_connection(conf->connection);
     conf->connection = NULL;
     return status;
@@ -845,6 +892,12 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */
   conf->vhost = NULL;
   conf->user = NULL;
   conf->password = NULL;
+  conf->tls_enabled = false;
+  conf->tls_verify_peer = true;
+  conf->tls_verify_hostname = true;
+  conf->tls_cacert = NULL;
+  conf->tls_client_cert = NULL;
+  conf->tls_client_key = NULL;
   conf->exchange = NULL;
   conf->routing_key = NULL;
   conf->connection_retry_delay = 0;
@@ -890,6 +943,18 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */
       status = cf_util_get_string(child, &conf->user);
     else if (strcasecmp("Password", child->key) == 0)
       status = cf_util_get_string(child, &conf->password);
+    else if (strcasecmp("TLSEnabled", child->key) == 0)
+      status = cf_util_get_boolean(child, &conf->tls_enabled);
+    else if (strcasecmp("TLSVerifyPeer", child->key) == 0)
+      status = cf_util_get_boolean(child, &conf->tls_verify_peer);
+    else if (strcasecmp("TLSVerifyHostName", child->key) == 0)
+      status = cf_util_get_boolean(child, &conf->tls_verify_hostname);
+    else if (strcasecmp("TLSCACert", child->key) == 0)
+      status = cf_util_get_string(child, &conf->tls_cacert);
+    else if (strcasecmp("TLSClientCert", child->key) == 0)
+      status = cf_util_get_string(child, &conf->tls_client_cert);
+    else if (strcasecmp("TLSClientKey", child->key) == 0)
+      status = cf_util_get_string(child, &conf->tls_client_key);
     else if (strcasecmp("Exchange", child->key) == 0)
       status = cf_util_get_string(child, &conf->exchange);
     else if (strcasecmp("ExchangeType", child->key) == 0)
@@ -959,6 +1024,29 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */
               "without the \"Exchange\" option. It will be ignored.");
   }
 
+#if !defined(AMQP_VERSION) || AMQP_VERSION < 0x00040000
+  if (status == 0 && conf->tls_enabled) {
+    ERROR("amqp plugin: TLSEnabled is set but not supported. "
+          "rebuild collectd with rabbitmq-c >= 0.4");
+    status = 1;
+  }
+#endif
+#if !defined(AMQP_VERSION) || AMQP_VERSION < 0x00080000
+  if (status == 0 && (!conf->tls_verify_peer || !conf->tls_verify_hostname)) {
+    ERROR("amqp plugin: disabling TLSVerify* is not supported. "
+          "rebuild collectd with rabbitmq-c >= 0.8");
+    status = 1;
+  }
+#endif
+  if (status == 0 &&
+      (conf->tls_client_cert != NULL || conf->tls_client_key != NULL)) {
+    if (conf->tls_client_cert == NULL || conf->tls_client_key == NULL) {
+      ERROR("amqp plugin: only one of TLSClientCert/TLSClientKey is "
+            "configured. need both or neither.");
+      status = 1;
+    }
+  }
+
   if (status != 0) {
     camqp_config_free(conf);
     return status;
index 9e30358b59660981dad6f3a67f10421a74c8dba0..aba046243866d802a0cc379ccd55d6ab2df207f0 100644 (file)
 #    Persistent false
 #    StoreRates false
 #    ConnectionRetryDelay 0
+#    TLSEnabled false
+#    TLSVerifyPeer true
+#    TLSVerifyHostName true
+#    TLSCACert "/path/to/ca.pem"
+#    TLSClientCert "/path/to/client-cert.pem"
+#    TLSClientKey "/path/to/client-key.pem"
 #  </Publish>
 #</Plugin>
 
index 7e761b45c0c7e159ffdfdeab49c49775ca8027ff..a72361192723a48b8cc7d1e6c2763dbef665ab34 100644 (file)
@@ -551,6 +551,12 @@ B<Synopsis:>
  #   ConnectionRetryDelay 0
  #   Format "command"
  #   StoreRates false
+ #   TLSEnabled false
+ #   TLSVerifyPeer true
+ #   TLSVerifyHostName true
+ #   TLSCACert "/path/to/ca.pem"
+ #   TLSClientCert "/path/to/client-cert.pem"
+ #   TLSClientKey "/path/to/client-key.pem"
  #   GraphitePrefix "collectd."
  #   GraphiteEscapeChar "_"
  #   GraphiteSeparateInstances false
@@ -572,6 +578,12 @@ B<Synopsis:>
  #   QueueAutoDelete true
  #   RoutingKey "collectd.#"
  #   ConnectionRetryDelay 0
+ #   TLSEnabled false
+ #   TLSVerifyPeer true
+ #   TLSVerifyHostName true
+ #   TLSCACert "/path/to/ca.pem"
+ #   TLSClientCert "/path/to/client-cert.pem"
+ #   TLSClientKey "/path/to/client-key.pem"
    </Subscribe>
  </Plugin>
 
@@ -736,6 +748,41 @@ If set to B<false> (the default) the C<.> (dot) character is replaced with
 I<GraphiteEscapeChar>. Otherwise, if set to B<true>, the C<.> (dot) character
 is preserved, i.e. passed through.
 
+=item B<TLSEnabled> B<true>|B<false>
+
+If set to B<true> then connect to the broker using a TLS connection.
+If set to B<false> (the default), then a plain text connection is used.
+
+Requires rabbitmq-c >= 0.4.
+
+=item B<TLSVerifyPeer> B<true>|B<false>
+
+If set to B<true> (the default) then the server certificate chain is verified.
+Setting this to B<false> will skip verification (insecure).
+
+Requires rabbitmq-c >= 0.8.
+
+=item B<TLSVerifyHostName> B<true>|B<false>
+
+If set to B<true> (the default) then the server host name is verified.
+Setting this to B<false> will skip verification (insecure).
+
+Requires rabbitmq-c >= 0.8.
+
+=item B<TLSCACert> I<Path>
+
+Path to the CA cert file in PEM format.
+
+=item B<TLSClientCert> I<Path>
+
+Path to the client certificate in PEM format.
+If this is set, then B<TLSClientKey> must be set as well.
+
+=item B<TLSClientKey> I<Path>
+
+Path to the client key in PEM format.
+If this is set, then B<TLSClientCert> must be set as well.
+
 =back
 
 =head2 Plugin C<amqp1>