Merge branch 'stable-2.10' into stable-2.11
authorHrvoje Ribicic <riba@google.com>
Mon, 30 Nov 2015 16:12:42 +0000 (17:12 +0100)
committerHrvoje Ribicic <riba@google.com>
Mon, 30 Nov 2015 16:27:40 +0000 (17:27 +0100)
* stable-2.10
  (no changes)

* stable-2.9
  QA: Ensure the DRBD secret is not retrievable via RAPI
  Redact the DRBD secret in instance queries
  Do not attempt to use the DRBD secret in gnt-instance info

Conflicts:
  qa/qa_rapi.py - simply append new changes

Signed-off-by: Hrvoje Ribicic <riba@google.com>
Reviewed-by: Klaus Aehlig <aehlig@google.com>

lib/client/gnt_instance.py
lib/cmdlib/instance_query.py
qa/ganeti-qa.py
qa/qa_rapi.py

index 1cf6e3f..bffa95c 100644 (file)
@@ -961,7 +961,6 @@ def _FormatDiskDetails(dev_type, dev, roman):
                  compat.TryToRoman(drbd_info["secondary_minor"],
                                    convert=roman))),
       ("port", str(compat.TryToRoman(drbd_info["port"], convert=roman))),
-      ("auth key", str(drbd_info["secret"])),
       ]
   elif dev_type == constants.DT_PLAIN:
     vg_name, lv_name = dev["logical_id"]
index d4a6847..2ae4c56 100644 (file)
@@ -164,6 +164,7 @@ class LUInstanceQueryData(NoHooksLU):
 
     """
     drbd_info = None
+    output_logical_id = dev.logical_id
     if dev.dev_type in constants.DTS_DRBD:
       # we change the snode then (otherwise we use the one passed in)
       if dev.logical_id[0] == instance.primary_node:
@@ -180,8 +181,9 @@ class LUInstanceQueryData(NoHooksLU):
         "secondary_node": node_uuid2name_fn(snode_uuid),
         "secondary_minor": snode_minor,
         "port": dev.logical_id[2],
-        "secret": dev.logical_id[5],
       }
+      # replace the secret present at the end of the ids with None
+      output_logical_id = dev.logical_id[:-1] + (None,)
 
     dev_pstatus = self._ComputeBlockdevStatus(instance.primary_node,
                                               instance, dev)
@@ -198,7 +200,7 @@ class LUInstanceQueryData(NoHooksLU):
     return {
       "iv_name": dev.iv_name,
       "dev_type": dev.dev_type,
-      "logical_id": dev.logical_id,
+      "logical_id": output_logical_id,
       "drbd_info": drbd_info,
       "pstatus": dev_pstatus,
       "sstatus": dev_sstatus,
index f3febb7..eacb499 100755 (executable)
@@ -837,6 +837,8 @@ def RunInstanceTests():
           RunExportImportTests(instance, inodes)
           RunHardwareFailureTests(instance, inodes)
           RunRepairDiskSizes()
+          RunTestIf(["rapi", "instance-data-censorship"],
+                    qa_rapi.TestInstanceDataCensorship, instance, inodes)
           RunTest(qa_instance.TestInstanceRemove, instance)
         finally:
           instance.Release()
index 00bc833..b2e7db0 100644 (file)
@@ -59,6 +59,7 @@ import qa_error
 import qa_logging
 import qa_utils
 
+from qa_instance import GetInstanceInfo
 from qa_instance import IsFailoverSupported
 from qa_instance import IsMigrationSupported
 from qa_instance import IsDiskReplacingSupported
@@ -1075,3 +1076,57 @@ def TestInterClusterInstanceMove(src_instance, dest_instance,
     if perform_checks:
       qa_utils.RunInstanceCheck(current_src_inst, False)
       qa_utils.RunInstanceCheck(current_dest_inst, True)
+
+
+_DRBD_SECRET_RE = re.compile('shared-secret.*"([0-9A-Fa-f]+)"')
+
+
+def _RetrieveSecret(instance, pnode):
+  """Retrieves the DRBD secret given an instance object and the primary node.
+
+  @type instance: L{qa_config._QaInstance}
+  @type pnode: L{qa_config._QaNode}
+
+  @rtype: string
+
+  """
+  instance_info = GetInstanceInfo(instance.name)
+
+  # We are interested in only the first disk on the primary
+  drbd_minor = instance_info["drbd-minors"][pnode.primary][0]
+
+  # This form should work for all DRBD versions
+  drbd_command = ("drbdsetup show %d; drbdsetup %d show || true" %
+                  (drbd_minor, drbd_minor))
+  instance_drbd_info = \
+    qa_utils.GetCommandOutput(pnode.primary, drbd_command)
+
+  match_obj = _DRBD_SECRET_RE.search(instance_drbd_info)
+  if match_obj is None:
+    raise qa_error.Error("Could not retrieve DRBD secret for instance %s from"
+                         " node %s." % (instance.name, pnode.primary))
+
+  return match_obj.groups(0)[0]
+
+
+def TestInstanceDataCensorship(instance, inodes):
+  """Test protection of sensitive instance data."""
+
+  if instance.disk_template != constants.DT_DRBD8:
+    print qa_utils.FormatInfo("Only the DRBD secret is a sensitive parameter"
+                              " right now, skipping for non-DRBD instance.")
+    return
+
+  drbd_secret = _RetrieveSecret(instance, inodes[0])
+
+  job_id = _rapi_client.GetInstanceInfo(instance.name)
+  if not _rapi_client.WaitForJobCompletion(job_id):
+    raise qa_error.Error("Could not fetch instance info for instance %s" %
+                         instance.name)
+  info_dict = _rapi_client.GetJobStatus(job_id)
+
+  if drbd_secret in str(info_dict):
+    print qa_utils.FormatInfo("DRBD secret: %s" % drbd_secret)
+    print qa_utils.FormatInfo("Retrieved data\n%s" % str(info_dict))
+    raise qa_error.Error("Found DRBD secret in contents of RAPI instance info"
+                         " call; see above.")