aboutsummaryrefslogtreecommitdiffstats
path: root/functest_kubernetes/security/security.py
blob: b878cb77a64dc1331101d52726dd966d8a54f78d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/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):
    # pylint: disable=too-many-instance-attributes
    """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
        self.output_log_name = 'functest-kubernetes.log'
        self.output_debug_log_name = 'functest-kubernetes.debug.log'

    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"