Skip to content

Commit

Permalink
Remove dynamic imports (#335)
Browse files Browse the repository at this point in the history
* Remove plugins
Current usage of plugins is not pluggable and includes logging
stuff.
Move this to conf/logging.
* Removed dynamic imports
* Add tests for hunters registration
  • Loading branch information
iyehuda authored Apr 12, 2020
1 parent 6d63f55 commit 14d73e2
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 63 deletions.
2 changes: 0 additions & 2 deletions kube_hunter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ First, let's go through kube-hunter's basic architecture.
### Directory Structure
~~~
kube-hunter/
plugins/
# your plugin
kube_hunter/
core/
modules/
Expand Down
2 changes: 0 additions & 2 deletions kube_hunter/conf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,3 @@
setup_logger(config.log)

__all__ = [config]

import plugins # noqa
4 changes: 4 additions & 0 deletions kube_hunter/conf/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
DEFAULT_LEVEL_NAME = logging.getLevelName(DEFAULT_LEVEL)
LOG_FORMAT = "%(asctime)s %(levelname)s %(name)s %(message)s"

# Suppress logging from scapy
logging.getLogger("scapy.runtime").setLevel(logging.CRITICAL)
logging.getLogger("scapy.loading").setLevel(logging.CRITICAL)


def setup_logger(level_name):
# Remove any existing handlers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,6 @@ def history(self):
return history


""" Event Types """
# TODO: make proof an abstract method.


class Service(object):
def __init__(self, name, path="", secure=True):
self.name = name
Expand Down Expand Up @@ -126,8 +122,6 @@ def get_severity(self):
event_id_count_lock = threading.Lock()
event_id_count = 0

""" Discovery/Hunting Events """


class NewHostEvent(Event):
def __init__(self, host, cloud=None):
Expand Down Expand Up @@ -195,9 +189,6 @@ class ReportDispatched(Event):
pass


""" Core Vulnerabilities """


class K8sVersionDisclosure(Vulnerability, Event):
"""The kubernetes version could be obtained from the {} endpoint """

Expand Down
10 changes: 0 additions & 10 deletions kube_hunter/core/events/types/__init__.py

This file was deleted.

27 changes: 20 additions & 7 deletions kube_hunter/modules/discovery/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
from os.path import dirname, basename, isfile
import glob
from . import (
apiserver,
dashboard,
etcd,
hosts,
kubectl,
kubelet,
ports,
proxy,
)

# dynamically importing all modules in folder
files = glob.glob(dirname(__file__) + "/*.py")
for module_name in (basename(f)[:-3] for f in files if isfile(f) and not f.endswith("__init__.py")):
if not module_name.startswith("test_"):
exec("from .{} import *".format(module_name))
__all__ = [
apiserver,
dashboard,
etcd,
hosts,
kubectl,
kubelet,
ports,
proxy,
]
36 changes: 30 additions & 6 deletions kube_hunter/modules/hunting/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
from os.path import dirname, basename, isfile
import glob
from . import (
aks,
apiserver,
arp,
capabilities,
certificates,
cves,
dashboard,
dns,
etcd,
kubelet,
mounts,
proxy,
secrets,
)

# dynamically importing all modules in folder
files = glob.glob(dirname(__file__) + "/*.py")
for module_name in (basename(f)[:-3] for f in files if isfile(f) and not f.endswith("__init__.py")):
exec(f"from .{module_name} import *")
__all__ = [
aks,
apiserver,
arp,
capabilities,
certificates,
cves,
dashboard,
dns,
etcd,
kubelet,
mounts,
proxy,
secrets,
]
14 changes: 0 additions & 14 deletions plugins/README.md

This file was deleted.

7 changes: 0 additions & 7 deletions plugins/__init__.py

This file was deleted.

5 changes: 0 additions & 5 deletions plugins/logging_mod.py

This file was deleted.

2 changes: 1 addition & 1 deletion tests/core/test_cloud.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import requests_mock
import json

from kube_hunter.core.events.types.common import NewHostEvent
from kube_hunter.core.events.types import NewHostEvent


# Testing if it doesn't try to run get_cloud if the cloud type is already set.
Expand Down
112 changes: 112 additions & 0 deletions tests/core/test_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
from kube_hunter.core.events.handler import handler
from kube_hunter.modules.discovery.apiserver import ApiServiceDiscovery
from kube_hunter.modules.discovery.dashboard import KubeDashboard as KubeDashboardDiscovery
from kube_hunter.modules.discovery.etcd import EtcdRemoteAccess as EtcdRemoteAccessDiscovery
from kube_hunter.modules.discovery.hosts import FromPodHostDiscovery, HostDiscovery
from kube_hunter.modules.discovery.kubectl import KubectlClientDiscovery
from kube_hunter.modules.discovery.kubelet import KubeletDiscovery
from kube_hunter.modules.discovery.ports import PortDiscovery
from kube_hunter.modules.discovery.proxy import KubeProxy as KubeProxyDiscovery
from kube_hunter.modules.hunting.aks import AzureSpnHunter, ProveAzureSpnExposure
from kube_hunter.modules.hunting.apiserver import (
AccessApiServer,
ApiVersionHunter,
AccessApiServerActive,
AccessApiServerWithToken,
)
from kube_hunter.modules.hunting.arp import ArpSpoofHunter
from kube_hunter.modules.hunting.capabilities import PodCapabilitiesHunter
from kube_hunter.modules.hunting.certificates import CertificateDiscovery
from kube_hunter.modules.hunting.cves import K8sClusterCveHunter, KubectlCVEHunter
from kube_hunter.modules.hunting.dashboard import KubeDashboard
from kube_hunter.modules.hunting.dns import DnsSpoofHunter
from kube_hunter.modules.hunting.etcd import EtcdRemoteAccess, EtcdRemoteAccessActive
from kube_hunter.modules.hunting.kubelet import (
ReadOnlyKubeletPortHunter,
SecureKubeletPortHunter,
ProveRunHandler,
ProveContainerLogsHandler,
ProveSystemLogs,
)
from kube_hunter.modules.hunting.mounts import VarLogMountHunter, ProveVarLogMount
from kube_hunter.modules.hunting.proxy import KubeProxy, ProveProxyExposed, K8sVersionDisclosureProve
from kube_hunter.modules.hunting.secrets import AccessSecrets

PASSIVE_HUNTERS = {
ApiServiceDiscovery,
KubeDashboardDiscovery,
EtcdRemoteAccessDiscovery,
FromPodHostDiscovery,
HostDiscovery,
KubectlClientDiscovery,
KubeletDiscovery,
PortDiscovery,
KubeProxyDiscovery,
AzureSpnHunter,
AccessApiServer,
AccessApiServerWithToken,
ApiVersionHunter,
PodCapabilitiesHunter,
CertificateDiscovery,
K8sClusterCveHunter,
KubectlCVEHunter,
KubeDashboard,
EtcdRemoteAccess,
ReadOnlyKubeletPortHunter,
SecureKubeletPortHunter,
VarLogMountHunter,
KubeProxy,
AccessSecrets,
}

ACTIVE_HUNTERS = {
ProveAzureSpnExposure,
AccessApiServerActive,
ArpSpoofHunter,
DnsSpoofHunter,
EtcdRemoteAccessActive,
ProveRunHandler,
ProveContainerLogsHandler,
ProveSystemLogs,
ProveVarLogMount,
ProveProxyExposed,
K8sVersionDisclosureProve,
}


def remove_test_hunters(hunters):
return {hunter for hunter in hunters if not hunter.__module__.startswith("test")}


def test_passive_hunters_registered():
expected_missing = set()
expected_odd = set()

registered_passive = remove_test_hunters(handler.passive_hunters.keys())
actual_missing = PASSIVE_HUNTERS - registered_passive
actual_odd = registered_passive - PASSIVE_HUNTERS

assert expected_missing == actual_missing, "Passive hunters are missing"
assert expected_odd == actual_odd, "Unexpected passive hunters are registered"


# TODO (#334): Active hunters registration cannot be tested since it requires `config.active` to be set
# def test_active_hunters_registered():
# expected_missing = set()
# expected_odd = set()
#
# registered_active = remove_test_hunters(handler.active_hunters.keys())
# actual_missing = ACTIVE_HUNTERS - registered_active
# actual_odd = registered_active - ACTIVE_HUNTERS
#
# assert expected_missing == actual_missing, "Active hunters are missing"
# assert expected_odd == actual_odd, "Unexpected active hunters are registered"


def test_all_hunters_registered():
# TODO: Enable active hunting mode in testing
# expected = PASSIVE_HUNTERS | ACTIVE_HUNTERS
expected = PASSIVE_HUNTERS
actual = remove_test_hunters(handler.all_hunters.keys())

assert expected == actual

0 comments on commit 14d73e2

Please sign in to comment.