From a98fdcb9a990db9d7410950dfad1ecd25af27cea Mon Sep 17 00:00:00 2001 From: Cédric Ollivier Date: Sat, 12 Sep 2020 13:52:47 +0200 Subject: Enhance kube-hunter result postprocessing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It fills self.details and checks if the test case passes according to criteria (severity = high by default) Change-Id: Ib20779b4b5dca078c65b546c8703bc99856c6f41 Signed-off-by: Cédric Ollivier (cherry picked from commit 4f197110710e9f148eae4533792e8e7e2d72f053) --- docker/security/testcases.yaml | 2 + functest_kubernetes/security/kube-hunter.yaml | 2 +- functest_kubernetes/security/security.py | 66 +++++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/docker/security/testcases.yaml b/docker/security/testcases.yaml index 892e552c..9636547e 100644 --- a/docker/security/testcases.yaml +++ b/docker/security/testcases.yaml @@ -16,6 +16,8 @@ tiers: vulnerabilities run: name: 'kube_hunter' + args: + severity: high - case_name: kube_bench project_name: functest diff --git a/functest_kubernetes/security/kube-hunter.yaml b/functest_kubernetes/security/kube-hunter.yaml index b4452a57..776ada70 100644 --- a/functest_kubernetes/security/kube-hunter.yaml +++ b/functest_kubernetes/security/kube-hunter.yaml @@ -10,6 +10,6 @@ spec: - name: kube-hunter image: aquasec/kube-hunter:0.3.1 command: ["python", "kube-hunter.py"] - args: ["--pod"] + args: ["--pod", "--report", "json", "--statistics", '--active'] restartPolicy: Never backoffLimit: 4 diff --git a/functest_kubernetes/security/security.py b/functest_kubernetes/security/security.py index 2051f956..d06ffb28 100644 --- a/functest_kubernetes/security/security.py +++ b/functest_kubernetes/security/security.py @@ -13,14 +13,17 @@ Define the parent for Kubernetes testing. from __future__ import division +import json import logging import time +import textwrap import yaml from kubernetes import client from kubernetes import config from kubernetes import watch import pkg_resources +import prettytable from xtesting.core import testcase @@ -37,6 +40,7 @@ class SecurityTesting(testcase.TestCase): self.corev1 = client.CoreV1Api() self.batchv1 = client.BatchV1Api() self.pod = None + self.pod_log = "" self.job_name = None self.output_log_name = 'functest-kubernetes.log' self.output_debug_log_name = 'functest-kubernetes.debug.log' @@ -75,10 +79,9 @@ class SecurityTesting(testcase.TestCase): pods = self.corev1.list_namespaced_pod( self.namespace, label_selector='job-name={}'.format(self.job_name)) self.pod = pods.items[0].metadata.name - api_response = self.corev1.read_namespaced_pod_log( + self.pod_log = self.corev1.read_namespaced_pod_log( name=self.pod, namespace=self.namespace) - self.__logger.warning("\n\n%s", api_response) - self.result = 100 + self.__logger.info("\n\n%s", self.pod_log) def run(self, **kwargs): assert self.job_name @@ -119,10 +122,63 @@ class KubeHunter(SecurityTesting): See https://github.com/aquasecurity/kube-hunter for more details """ + __logger = logging.getLogger(__name__) + def __init__(self, **kwargs): super(KubeHunter, self).__init__(**kwargs) self.job_name = "kube-hunter" + def process_results(self, **kwargs): + """Process kube-hunter details""" + self.details = json.loads(self.pod_log.splitlines()[-1]) + if self.details["vulnerabilities"]: + self.result = 100 + msg = prettytable.PrettyTable( + header_style='upper', padding_width=5, + field_names=['category', 'vulnerability', 'severity']) + severity = kwargs.get("severity", "high") + if severity == "low": + allowed_severity = [] + elif severity == "medium": + allowed_severity = ["low"] + elif severity == "high": + allowed_severity = ["low", "medium"] + else: + self.__logger.warning( + "Selecting high as default severity (%s is incorrect)", + kwargs.get("severity", "high")) + severity = "high" + allowed_severity = ["low", "medium"] + for vulnerability in self.details["vulnerabilities"]: + if vulnerability["severity"] in allowed_severity: + self.__logger.warning( + "Skipping %s (severity is configured as %s)", + vulnerability["vulnerability"], severity) + else: + self.result = 0 + msg.add_row( + [vulnerability["category"], vulnerability["vulnerability"], + vulnerability["severity"]]) + self.__logger.warning("\n\n%s\n", msg.get_string()) + if self.details["hunter_statistics"]: + msg = prettytable.PrettyTable( + header_style='upper', padding_width=5, + field_names=['name', 'description', 'vulnerabilities']) + for statistics in self.details["hunter_statistics"]: + msg.add_row( + [statistics["name"], + textwrap.fill(statistics["description"], width=50), + statistics["vulnerabilities"]]) + self.__logger.info("\n\n%s\n", msg.get_string()) + + def run(self, **kwargs): + super(KubeHunter, self).run(**kwargs) + try: + self.process_results(**kwargs) + except Exception: # pylint: disable=broad-except + self.__logger.exception("Cannot process results") + self.result = 0 + class KubeBench(SecurityTesting): """kube-bench checks whether Kubernetes is deployed securelyself. @@ -135,3 +191,7 @@ class KubeBench(SecurityTesting): def __init__(self, **kwargs): super(KubeBench, self).__init__(**kwargs) self.job_name = "kube-bench" + + def run(self, **kwargs): + super(KubeBench, self).run(**kwargs) + self.result = 100 -- cgit 1.2.3-korg