From 98d9f93337ab514fa9aafc1cd1e87473de68b364 Mon Sep 17 00:00:00 2001 From: mrichomme Date: Mon, 10 Feb 2020 17:49:43 +0100 Subject: Add security docker for functest-kubernetes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit run kube-hunter and kube-bench cases dealing with security in kubernetes (check vulnerabilities) [1][2] It's the first step only printing the output. [1]: https://github.com/aquasecurity/kube-bench [2]: https://github.com/aquasecurity/kube-hunter Co-Authored-By: Cédric Ollivier Change-Id: I3bd9bda80046ef7a0c494d51dfb0b8cbfea02bb0 Signed-off-by: mrichomme --- functest_kubernetes/security/__init__.py | 0 functest_kubernetes/security/kube-bench.yaml | 51 +++++++++++ functest_kubernetes/security/kube-hunter.yaml | 14 +++ functest_kubernetes/security/security.py | 121 ++++++++++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 functest_kubernetes/security/__init__.py create mode 100644 functest_kubernetes/security/kube-bench.yaml create mode 100644 functest_kubernetes/security/kube-hunter.yaml create mode 100644 functest_kubernetes/security/security.py (limited to 'functest_kubernetes') diff --git a/functest_kubernetes/security/__init__.py b/functest_kubernetes/security/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/functest_kubernetes/security/kube-bench.yaml b/functest_kubernetes/security/kube-bench.yaml new file mode 100644 index 00000000..ec42ba16 --- /dev/null +++ b/functest_kubernetes/security/kube-bench.yaml @@ -0,0 +1,51 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: kube-bench +spec: + template: + metadata: + labels: + app: kube-bench + spec: + hostPID: true + containers: + - name: kube-bench + image: aquasec/kube-bench:latest + command: ["kube-bench"] + volumeMounts: + - name: var-lib-etcd + mountPath: /var/lib/etcd + readOnly: true + - name: var-lib-kubelet + mountPath: /var/lib/kubelet + readOnly: true + - name: etc-systemd + mountPath: /etc/systemd + readOnly: true + - name: etc-kubernetes + mountPath: /etc/kubernetes + readOnly: true + # /usr/local/mount-from-host/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version. + # You can omit this mount if you specify --version as part of the command. + - name: usr-bin + mountPath: /usr/local/mount-from-host/bin + readOnly: true + restartPolicy: Never + volumes: + - name: var-lib-etcd + hostPath: + path: "/var/lib/etcd" + - name: var-lib-kubelet + hostPath: + path: "/var/lib/kubelet" + - name: etc-systemd + hostPath: + path: "/etc/systemd" + - name: etc-kubernetes + hostPath: + path: "/etc/kubernetes" + - name: usr-bin + hostPath: + path: "/usr/bin" diff --git a/functest_kubernetes/security/kube-hunter.yaml b/functest_kubernetes/security/kube-hunter.yaml new file mode 100644 index 00000000..ce88c062 --- /dev/null +++ b/functest_kubernetes/security/kube-hunter.yaml @@ -0,0 +1,14 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: kube-hunter +spec: + template: + spec: + containers: + - name: kube-hunter + image: aquasec/kube-hunter + command: ["python", "kube-hunter.py"] + args: ["--pod"] + restartPolicy: Never + backoffLimit: 4 diff --git a/functest_kubernetes/security/security.py b/functest_kubernetes/security/security.py new file mode 100644 index 00000000..33e70f86 --- /dev/null +++ b/functest_kubernetes/security/security.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python + +# Copyright (c) 2020 Orange and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +""" +Define the parent for Kubernetes testing. +""" + +from __future__ import division + +import logging +import time +import yaml + +from kubernetes import client +from kubernetes import config +from kubernetes import watch +import pkg_resources +from xtesting.core import testcase + + +class SecurityTesting(testcase.TestCase): + """Run Security job""" + namespace = 'default' + watch_timeout = 1200 + + __logger = logging.getLogger(__name__) + + def __init__(self, **kwargs): + super(SecurityTesting, self).__init__(**kwargs) + config.load_kube_config() + self.corev1 = client.CoreV1Api() + self.batchv1 = client.BatchV1Api() + self.pod = None + self.job_name = None + + def deploy_job(self): + """Run Security job + + It runs a single security job and then simply prints its output asis. + """ + + assert self.job_name + with open(pkg_resources.resource_filename( + "functest_kubernetes", + "security/{}.yaml".format(self.job_name))) as yfile: + body = yaml.safe_load(yfile) + api_response = self.batchv1.create_namespaced_job( + body=body, namespace="default") + self.__logger.info("Job %s created", api_response.metadata.name) + self.__logger.debug("create_namespaced_job: %s", api_response) + watch_job = watch.Watch() + for event in watch_job.stream( + func=self.batchv1.list_namespaced_job, + namespace=self.namespace, timeout_seconds=self.watch_timeout): + if (event["object"].metadata.name == self.job_name and + event["object"].status.succeeded == 1): + self.__logger.info( + "%s started in %0.2f sec", event['object'].metadata.name, + time.time()-self.start_time) + watch_job.stop() + 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( + name=self.pod, namespace=self.namespace) + self.__logger.warning("\n\n%s", api_response) + self.result = 100 + + def run(self, **kwargs): + assert self.job_name + self.start_time = time.time() + try: + self.deploy_job() + except client.rest.ApiException: + self.__logger.exception("Cannot run %s", self.job_name) + self.stop_time = time.time() + + def clean(self): + try: + api_response = self.corev1.delete_namespaced_pod( + name=self.pod, namespace=self.namespace) + self.__logger.debug("delete_namespaced_pod: %s", api_response) + except client.rest.ApiException: + pass + try: + api_response = self.batchv1.delete_namespaced_job( + name=self.job_name, namespace=self.namespace) + self.__logger.debug( + "delete_namespaced_deployment: %s", api_response) + except client.rest.ApiException: + pass + + +class KubeHunter(SecurityTesting): + """kube-hunter hunts for security weaknesses in Kubernetes clusters. + + See https://github.com/aquasecurity/kube-hunter for more details + """ + + def __init__(self, **kwargs): + super(KubeHunter, self).__init__(**kwargs) + self.job_name = "kube-hunter" + + +class KubeBench(SecurityTesting): + """kube-bench checks whether Kubernetes is deployed securelyself. + + It runs the checks documented in the CIS Kubernetes Benchmark. + + See https://github.com/aquasecurity/kube-bench for more details + """ + + def __init__(self, **kwargs): + super(KubeBench, self).__init__(**kwargs) + self.job_name = "kube-bench" -- cgit 1.2.3-korg