Blob Blame History Raw
From 46b817b8c7e7d66ee91f9f47c00e96d5afa8a18e Mon Sep 17 00:00:00 2001
From: Kyle Lape <klape@redhat.com>
Date: Fri, 23 Feb 2018 12:52:04 -0600
Subject: [PATCH] GPG verify egg in parent process

This fixes issue #54.

- Add GPG verification code (instead of relying on code from egg)

- Enable bypass via environment variable BYPASS_GPG.  The process only
  recognizes the value "true" (case insensitive); all other values resolve
  to false.  This only applies to the GPG verification done in the parent
  process; eggs loaded in subprocess (i.e. phases) still use --no-gpg.

- If last_stable.egg cannot be GPG verified, try rpm.egg.  If rpm.egg is
  verified, use it instead of last_stable.egg.  If rpm.egg cannot be
  verified, either, then print an error message and exit.

- A debug logging statement is added after logging has been set up to
  indicate which egg was loaded in the parent process.
  Look for string: "Loaded initial egg".
---
 insights_client/__init__.py | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/insights_client/__init__.py b/insights_client/__init__.py
index d8932f5..fd2adf0 100644
--- a/insights_client/__init__.py
+++ b/insights_client/__init__.py
@@ -9,9 +9,14 @@ import grp
 import os
 import sys
 import subprocess
+from subprocess import PIPE
+import shlex
 import logging
 import logging.handlers
 
+GPG_KEY = "/etc/insights-client/redhattools.pub.gpg"
+
+BYPASS_GPG = os.environ.get("BYPASS_GPG", "").lower() == "true"
 ENV_EGG = os.environ.get("EGG")
 NEW_EGG = "/var/lib/insights/newest.egg"
 STABLE_EGG = "/var/lib/insights/last_stable.egg"
@@ -44,6 +49,17 @@ def demote(uid, gid, run_as_root):
         return result
 
 
+def gpg_validate(path):
+    if BYPASS_GPG:
+        return True
+
+    gpg_template = '/usr/bin/gpg --verify --keyring %s %s %s'
+    cmd = gpg_template % (GPG_KEY, path + '.asc', path)
+    proc = subprocess.Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
+    proc.communicate()
+    return proc.returncode == 0
+
+
 def run_phase(phase, client):
     """
     Call the run script for the given phase.  If the phase succeeds returns the
@@ -106,10 +122,16 @@ def _main():
     if not all(map(None, [insights_uid, insights_gid, insights_grpid])):
         sys.exit("User and/or group 'insights' not found. Exiting.")
 
-    sys.path = [STABLE_EGG, RPM_EGG] + sys.path
+    validated_eggs = filter(gpg_validate, [STABLE_EGG, RPM_EGG])
+
+    if not validated_eggs:
+        sys.exit("No GPG-verified eggs can be found")
+
+    sys.path = validated_eggs + sys.path
 
     try:
         # flake8 complains because these imports aren't at the top
+        import insights
         from insights.client import InsightsClient
         from insights.client.phase.v1 import get_phases
 
@@ -124,6 +146,7 @@ def _main():
             log_handler.doRollover()
         # we now have access to the clients logging mechanism instead of using print
         client.set_up_logging()
+        logging.root.debug("Loaded initial egg: %s", os.path.dirname(insights.__file__))
 
         # check for insights user/group
         if not (insights_uid or insights_gid):
-- 
2.14.3