Repository: DataDog/dd-agent

Issue:
Use /bin/false as a login shell for dd-agent
Unless there is a compelling need not to.

<!---
@huboard:{"order":386.5}
-->

Turn SSL certificate validation into an option of http_check.py
- Leave it to False by default
- Make it a per-site option in the yaml file
- Log a warning when it is set to False

<!---
@huboard:{"order":769.25}
-->



Discussion:

Resolving patch:
diff --git a/checks.d/http_check.py b/checks.d/http_check.py
--- a/checks.d/http_check.py
+++ b/checks.d/http_check.py
@@ -18,15 +18,18 @@ def _load_conf(self, instance):
         if url is None:
             raise Exception("Bad configuration. You must specify a url")
         include_content = instance.get('include_content', False)
-        return url, username, password, timeout, include_content, headers, response_time, tags
+        ssl = instance.get('disable_ssl_validation', True)
+        return url, username, password, timeout, include_content, headers, response_time, tags, ssl
 
     def _check(self, instance):
-        addr, username, password, timeout, include_content, headers, response_time, tags = self._load_conf(instance)
+        addr, username, password, timeout, include_content, headers, response_time, tags, disable_ssl_validation = self._load_conf(instance)
         content = ''
         start = time.time()
         try:
             self.log.debug("Connecting to %s" % addr)
-            h = Http(timeout=timeout, disable_ssl_certificate_validation=True)
+            if disable_ssl_validation:
+                self.warning("Skipping SSL certificate validation for %s based on configuration" % addr)
+            h = Http(timeout=timeout, disable_ssl_certificate_validation=disable_ssl_validation)
             if username is not None and password is not None:
                 h.add_credentials(username, password)
             resp, content = h.request(addr, "GET", headers=headers)
diff --git a/graphite.py b/graphite.py
--- a/graphite.py
+++ b/graphite.py
@@ -1,9 +1,8 @@
-import sys, os, re, struct
+import struct
 import logging
 import cPickle as pickle
 
 from tornado.ioloop import IOLoop
-from tornado.iostream import IOStream
 
 log = logging.getLogger(__name__)
 
@@ -17,7 +16,9 @@
 class GraphiteServer(TCPServer):
 
     def __init__(self, app, hostname, io_loop=None, ssl_options=None, **kwargs):
-        log.info('Graphite listener is started')
+        log.warn('Graphite listener is started -- if you do not need graphite, turn it off in datadog.conf.')
+        log.warn('Graphite relay uses pickle to transport messages. Pickle is not secured against remote execution exploits.')
+        log.warn('See http://blog.nelhage.com/2011/03/exploiting-pickle/ for more details')
         self.app = app
         self.hostname = hostname
         TCPServer.__init__(self, io_loop=io_loop, ssl_options=ssl_options, **kwargs)
@@ -37,9 +38,9 @@ def __init__(self, stream, address, app, hostname):
         self.stream.set_close_callback(self._on_close)
         self.stream.read_bytes(4, self._on_read_header)
 
-    def _on_read_header(self,data):
+    def _on_read_header(self, data):
         try:
-            size = struct.unpack("!L",data)[0]
+            size = struct.unpack("!L", data)[0]
             log.debug("Receiving a string of size:" + str(size))
             self.stream.read_bytes(size, self._on_read_line)
         except Exception, e:
@@ -84,12 +85,12 @@ def _processMetric(self, metric, datapoint):
             send the datapoint to datadog"""
 
         log.debug("New metric: %s, values: %s" % (metric, datapoint))
-        (metric,host,device) = self._parseMetric(metric)
+        (metric, host, device) = self._parseMetric(metric)
         if metric is not None:    
-            self._postMetric(metric,host,device, datapoint)
+            self._postMetric(metric, host, device, datapoint)
             log.info("Posted metric: %s, host: %s, device: %s" % (metric, host, device))
 
-    def _decode(self,data):
+    def _decode(self, data):
 
         try:
             datapoints = pickle.loads(data)
@@ -99,12 +100,12 @@ def _decode(self,data):
    
         for (metric, datapoint) in datapoints:
             try:
-                datapoint = ( float(datapoint[0]), float(datapoint[1]) )
+                datapoint = (float(datapoint[0]), float(datapoint[1]))
             except Exception, e:
                 log.error(e)
                 continue
             
-            self._processMetric(metric,datapoint) 
+            self._processMetric(metric, datapoint)
 
         self.stream.read_bytes(4, self._on_read_header)
 
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -239,7 +239,6 @@ def get_tags():
             EC2_tags = None
             pass
 
-
         try:
             if socket_to is None:
                 socket_to = 3


Test patch:
diff --git a/tests/test_ganglia.py b/tests/test_ganglia.py
--- a/tests/test_ganglia.py
+++ b/tests/test_ganglia.py
@@ -9,6 +9,8 @@
 import tempfile
 from util import json, md5
 import time
+import xml.etree.ElementTree as tree
+from cStringIO import StringIO
 
 from checks.ganglia import Ganglia
 
@@ -27,7 +29,13 @@ def testSpeed(self):
         # profile.runctx("g.check({'ganglia_host': 'localhost', 'ganglia_port': 8651})", {}, {"g": g}, pfile.name)
         # p = pstats.Stats(pfile.name)
         # p.sort_stats('time').print_stats()
-        self.assertEquals(md5(g.check({'ganglia_host': 'localhost', 'ganglia_port': 8651})).hexdigest(), md5(open(TEST_FN).read()).hexdigest())
+        parsed = StringIO(g.check({'ganglia_host': 'localhost', 'ganglia_port': 8651}))
+        original = open(TEST_FN)
+        x1 = tree.parse(parsed)
+        x2 = tree.parse(original)
+        # Cursory test
+        self.assertEquals([c.tag for c in x1.getroot()], [c.tag for c in x2.getroot()])
+
 
 if __name__ == '__main__':
     unittest.main()