mirror of
https://github.com/aquasecurity/kube-hunter.git
synced 2026-08-23 22:26:23 +00:00
Merge branch 'master' into fix-hunting-bugs
This commit is contained in:
@@ -4,7 +4,7 @@ from typing import Any, Optional
|
||||
|
||||
@dataclass
|
||||
class Config:
|
||||
""" Config is a configuration container.
|
||||
"""Config is a configuration container.
|
||||
It contains the following fields:
|
||||
- active: Enable active hunters
|
||||
- cidr: Network subnets to scan
|
||||
|
||||
@@ -9,7 +9,9 @@ def parser_add_arguments(parser):
|
||||
Contains initialization for all default arguments
|
||||
"""
|
||||
parser.add_argument(
|
||||
"--list", action="store_true", help="Displays all tests in kubehunter (add --active flag to see active tests)",
|
||||
"--list",
|
||||
action="store_true",
|
||||
help="Displays all tests in kubehunter (add --active flag to see active tests)",
|
||||
)
|
||||
|
||||
parser.add_argument("--interface", action="store_true", help="Set hunting on all network interfaces")
|
||||
@@ -19,7 +21,9 @@ def parser_add_arguments(parser):
|
||||
parser.add_argument("--quick", action="store_true", help="Prefer quick scan (subnet 24)")
|
||||
|
||||
parser.add_argument(
|
||||
"--include-patched-versions", action="store_true", help="Don't skip patched versions when scanning",
|
||||
"--include-patched-versions",
|
||||
action="store_true",
|
||||
help="Don't skip patched versions when scanning",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -29,11 +33,17 @@ def parser_add_arguments(parser):
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--mapping", action="store_true", help="Outputs only a mapping of the cluster's nodes",
|
||||
"--mapping",
|
||||
action="store_true",
|
||||
help="Outputs only a mapping of the cluster's nodes",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--remote", nargs="+", metavar="HOST", default=list(), help="One or more remote ip/dns to hunt",
|
||||
"--remote",
|
||||
nargs="+",
|
||||
metavar="HOST",
|
||||
default=list(),
|
||||
help="One or more remote ip/dns to hunt",
|
||||
)
|
||||
|
||||
parser.add_argument("--active", action="store_true", help="Enables active hunting")
|
||||
@@ -47,7 +57,10 @@ def parser_add_arguments(parser):
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--report", type=str, default="plain", help="Set report type, options are: plain, yaml, json",
|
||||
"--report",
|
||||
type=str,
|
||||
default="plain",
|
||||
help="Set report type, options are: plain, yaml, json",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
|
||||
@@ -144,7 +144,8 @@ class NewHostEvent(Event):
|
||||
logger.debug("Checking whether the cluster is deployed on azure's cloud")
|
||||
# Leverage 3rd tool https://github.com/blrchen/AzureSpeed for Azure cloud ip detection
|
||||
result = requests.get(
|
||||
f"https://api.azurespeed.com/api/region?ipOrUrl={self.host}", timeout=config.network_timeout,
|
||||
f"https://api.azurespeed.com/api/region?ipOrUrl={self.host}",
|
||||
timeout=config.network_timeout,
|
||||
).json()
|
||||
return result["cloud"] or "NoCloud"
|
||||
except requests.ConnectionError:
|
||||
@@ -194,7 +195,11 @@ class K8sVersionDisclosure(Vulnerability, Event):
|
||||
|
||||
def __init__(self, version, from_endpoint, extra_info=""):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, "K8s Version Disclosure", category=InformationDisclosure, vid="KHV002",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
"K8s Version Disclosure",
|
||||
category=InformationDisclosure,
|
||||
vid="KHV002",
|
||||
)
|
||||
self.version = version
|
||||
self.from_endpoint = from_endpoint
|
||||
|
||||
@@ -46,7 +46,11 @@ class AzureMetadataApi(Vulnerability, Event):
|
||||
|
||||
def __init__(self, cidr):
|
||||
Vulnerability.__init__(
|
||||
self, Azure, "Azure Metadata Exposure", category=InformationDisclosure, vid="KHV003",
|
||||
self,
|
||||
Azure,
|
||||
"Azure Metadata Exposure",
|
||||
category=InformationDisclosure,
|
||||
vid="KHV003",
|
||||
)
|
||||
self.cidr = cidr
|
||||
self.evidence = "cidr: {}".format(cidr)
|
||||
@@ -140,7 +144,9 @@ class FromPodHostDiscovery(Discovery):
|
||||
def traceroute_discovery(self):
|
||||
config = get_config()
|
||||
node_internal_ip = srp1(
|
||||
Ether() / IP(dst="1.1.1.1", ttl=1) / ICMP(), verbose=0, timeout=config.network_timeout,
|
||||
Ether() / IP(dst="1.1.1.1", ttl=1) / ICMP(),
|
||||
verbose=0,
|
||||
timeout=config.network_timeout,
|
||||
)[IP].src
|
||||
return [[node_internal_ip, "24"]]
|
||||
|
||||
|
||||
@@ -16,7 +16,11 @@ class AzureSpnExposure(Vulnerability, Event):
|
||||
|
||||
def __init__(self, container):
|
||||
Vulnerability.__init__(
|
||||
self, Azure, "Azure SPN Exposure", category=IdentityTheft, vid="KHV004",
|
||||
self,
|
||||
Azure,
|
||||
"Azure SPN Exposure",
|
||||
category=IdentityTheft,
|
||||
vid="KHV004",
|
||||
)
|
||||
self.container = container
|
||||
|
||||
|
||||
@@ -29,7 +29,11 @@ class ServerApiAccess(Vulnerability, Event):
|
||||
name = "Unauthenticated access to API"
|
||||
category = UnauthenticatedAccess
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name=name, category=category, vid="KHV005",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name=name,
|
||||
category=category,
|
||||
vid="KHV005",
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -42,7 +46,11 @@ class ServerApiHTTPAccess(Vulnerability, Event):
|
||||
name = "Insecure (HTTP) access to API"
|
||||
category = UnauthenticatedAccess
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name=name, category=category, vid="KHV006",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name=name,
|
||||
category=category,
|
||||
vid="KHV006",
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -54,7 +62,11 @@ class ApiInfoDisclosure(Vulnerability, Event):
|
||||
else:
|
||||
name += " as anonymous user"
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name=name, category=InformationDisclosure, vid="KHV007",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name=name,
|
||||
category=InformationDisclosure,
|
||||
vid="KHV007",
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -89,12 +101,14 @@ class ListClusterRoles(ApiInfoDisclosure):
|
||||
|
||||
class CreateANamespace(Vulnerability, Event):
|
||||
|
||||
""" Creating a namespace might give an attacker an area with default (exploitable) permissions to run pods in.
|
||||
"""
|
||||
"""Creating a namespace might give an attacker an area with default (exploitable) permissions to run pods in."""
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Created a namespace", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Created a namespace",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -105,14 +119,17 @@ class DeleteANamespace(Vulnerability, Event):
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Delete a namespace", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Delete a namespace",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
|
||||
class CreateARole(Vulnerability, Event):
|
||||
""" Creating a role might give an attacker the option to harm the normal behavior of newly created pods
|
||||
within the specified namespaces.
|
||||
"""Creating a role might give an attacker the option to harm the normal behavior of newly created pods
|
||||
within the specified namespaces.
|
||||
"""
|
||||
|
||||
def __init__(self, evidence):
|
||||
@@ -121,37 +138,46 @@ class CreateARole(Vulnerability, Event):
|
||||
|
||||
|
||||
class CreateAClusterRole(Vulnerability, Event):
|
||||
""" Creating a cluster role might give an attacker the option to harm the normal behavior of newly created pods
|
||||
across the whole cluster
|
||||
"""Creating a cluster role might give an attacker the option to harm the normal behavior of newly created pods
|
||||
across the whole cluster
|
||||
"""
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Created a cluster role", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Created a cluster role",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
|
||||
class PatchARole(Vulnerability, Event):
|
||||
""" Patching a role might give an attacker the option to create new pods with custom roles within the
|
||||
"""Patching a role might give an attacker the option to create new pods with custom roles within the
|
||||
specific role's namespace scope
|
||||
"""
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Patched a role", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Patched a role",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
|
||||
class PatchAClusterRole(Vulnerability, Event):
|
||||
""" Patching a cluster role might give an attacker the option to create new pods with custom roles within the whole
|
||||
"""Patching a cluster role might give an attacker the option to create new pods with custom roles within the whole
|
||||
cluster scope.
|
||||
"""
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Patched a cluster role", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Patched a cluster role",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -161,7 +187,10 @@ class DeleteARole(Vulnerability, Event):
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Deleted a role", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Deleted a role",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -171,7 +200,10 @@ class DeleteAClusterRole(Vulnerability, Event):
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Deleted a cluster role", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Deleted a cluster role",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -181,7 +213,10 @@ class CreateAPod(Vulnerability, Event):
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Created A Pod", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Created A Pod",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -191,7 +226,10 @@ class CreateAPrivilegedPod(Vulnerability, Event):
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Created A PRIVILEGED Pod", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Created A PRIVILEGED Pod",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -201,7 +239,10 @@ class PatchAPod(Vulnerability, Event):
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Patched A Pod", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Patched A Pod",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -211,7 +252,10 @@ class DeleteAPod(Vulnerability, Event):
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Deleted A Pod", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Deleted A Pod",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -225,7 +269,7 @@ class ApiServerPassiveHunterFinished(Event):
|
||||
# If we have a service account token we'll also trigger AccessApiServerWithToken below
|
||||
@handler.subscribe(ApiServer)
|
||||
class AccessApiServer(Hunter):
|
||||
""" API Server Hunter
|
||||
"""API Server Hunter
|
||||
Checks if API server is accessible
|
||||
"""
|
||||
|
||||
@@ -268,7 +312,10 @@ class AccessApiServer(Hunter):
|
||||
try:
|
||||
if not namespace:
|
||||
r = requests.get(
|
||||
f"{self.path}/api/v1/pods", headers=self.headers, verify=False, timeout=config.network_timeout,
|
||||
f"{self.path}/api/v1/pods",
|
||||
headers=self.headers,
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
)
|
||||
else:
|
||||
r = requests.get(
|
||||
@@ -319,7 +366,7 @@ class AccessApiServer(Hunter):
|
||||
|
||||
@handler.subscribe(ApiServer, predicate=lambda x: x.auth_token)
|
||||
class AccessApiServerWithToken(AccessApiServer):
|
||||
""" API Server Hunter
|
||||
"""API Server Hunter
|
||||
Accessing the API server using the service account token obtained from a compromised pod
|
||||
"""
|
||||
|
||||
@@ -411,7 +458,8 @@ class AccessApiServerActive(ActiveHunter):
|
||||
def patch_a_pod(self, namespace, pod_name):
|
||||
data = [{"op": "add", "path": "/hello", "value": ["world"]}]
|
||||
return self.patch_item(
|
||||
path=f"{self.path}/api/v1/namespaces/{namespace}/pods/{pod_name}", data=json.dumps(data),
|
||||
path=f"{self.path}/api/v1/namespaces/{namespace}/pods/{pod_name}",
|
||||
data=json.dumps(data),
|
||||
)
|
||||
|
||||
def create_namespace(self):
|
||||
@@ -438,7 +486,8 @@ class AccessApiServerActive(ActiveHunter):
|
||||
"rules": [{"apiGroups": [""], "resources": ["pods"], "verbs": ["get", "watch", "list"]}],
|
||||
}
|
||||
return self.create_item(
|
||||
path=f"{self.path}/apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/roles", data=json.dumps(role),
|
||||
path=f"{self.path}/apis/rbac.authorization.k8s.io/v1/namespaces/{namespace}/roles",
|
||||
data=json.dumps(role),
|
||||
)
|
||||
|
||||
def create_a_cluster_role(self):
|
||||
@@ -450,7 +499,8 @@ class AccessApiServerActive(ActiveHunter):
|
||||
"rules": [{"apiGroups": [""], "resources": ["pods"], "verbs": ["get", "watch", "list"]}],
|
||||
}
|
||||
return self.create_item(
|
||||
path=f"{self.path}/apis/rbac.authorization.k8s.io/v1/clusterroles", data=json.dumps(cluster_role),
|
||||
path=f"{self.path}/apis/rbac.authorization.k8s.io/v1/clusterroles",
|
||||
data=json.dumps(cluster_role),
|
||||
)
|
||||
|
||||
def delete_a_role(self, namespace, name):
|
||||
@@ -477,7 +527,8 @@ class AccessApiServerActive(ActiveHunter):
|
||||
def patch_a_cluster_role(self, cluster_role):
|
||||
data = [{"op": "add", "path": "/hello", "value": ["world"]}]
|
||||
return self.patch_item(
|
||||
path=f"{self.path}/apis/rbac.authorization.k8s.io/v1/clusterroles/{cluster_role}", data=json.dumps(data),
|
||||
path=f"{self.path}/apis/rbac.authorization.k8s.io/v1/clusterroles/{cluster_role}",
|
||||
data=json.dumps(data),
|
||||
)
|
||||
|
||||
def execute(self):
|
||||
|
||||
@@ -17,7 +17,11 @@ class PossibleArpSpoofing(Vulnerability, Event):
|
||||
|
||||
def __init__(self):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, "Possible Arp Spoof", category=IdentityTheft, vid="KHV020",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
"Possible Arp Spoof",
|
||||
category=IdentityTheft,
|
||||
vid="KHV020",
|
||||
)
|
||||
|
||||
|
||||
@@ -55,7 +59,9 @@ class ArpSpoofHunter(ActiveHunter):
|
||||
config = get_config()
|
||||
self_ip = sr1(IP(dst="1.1.1.1", ttl=1) / ICMP(), verbose=0, timeout=config.network_timeout)[IP].dst
|
||||
arp_responses, _ = srp(
|
||||
Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(op=1, pdst=f"{self_ip}/24"), timeout=config.network_timeout, verbose=0,
|
||||
Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(op=1, pdst=f"{self_ip}/24"),
|
||||
timeout=config.network_timeout,
|
||||
verbose=0,
|
||||
)
|
||||
|
||||
# arp enabled on cluster and more than one pod on node
|
||||
|
||||
@@ -17,7 +17,10 @@ class CapNetRawEnabled(Event, Vulnerability):
|
||||
|
||||
def __init__(self):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="CAP_NET_RAW Enabled", category=AccessRisk,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="CAP_NET_RAW Enabled",
|
||||
category=AccessRisk,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,11 @@ class CertificateEmail(Vulnerability, Event):
|
||||
|
||||
def __init__(self, email):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, "Certificate Includes Email Address", category=InformationDisclosure, vid="KHV021",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
"Certificate Includes Email Address",
|
||||
category=InformationDisclosure,
|
||||
vid="KHV021",
|
||||
)
|
||||
self.email = email
|
||||
self.evidence = "email: {}".format(self.email)
|
||||
|
||||
@@ -33,7 +33,7 @@ class ServerApiVersionEndPointAccessPE(Vulnerability, Event):
|
||||
|
||||
class ServerApiVersionEndPointAccessDos(Vulnerability, Event):
|
||||
"""Node not patched for CVE-2019-1002100. Depending on your RBAC settings,
|
||||
a crafted json-patch could cause a Denial of Service."""
|
||||
a crafted json-patch could cause a Denial of Service."""
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
@@ -52,7 +52,11 @@ class PingFloodHttp2Implementation(Vulnerability, Event):
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Possible Ping Flood Attack", category=DenialOfService, vid="KHV024",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Possible Ping Flood Attack",
|
||||
category=DenialOfService,
|
||||
vid="KHV024",
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -63,7 +67,11 @@ class ResetFloodHttp2Implementation(Vulnerability, Event):
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Possible Reset Flood Attack", category=DenialOfService, vid="KHV025",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Possible Reset Flood Attack",
|
||||
category=DenialOfService,
|
||||
vid="KHV025",
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
@@ -89,7 +97,11 @@ class IncompleteFixToKubectlCpVulnerability(Vulnerability, Event):
|
||||
|
||||
def __init__(self, binary_version):
|
||||
Vulnerability.__init__(
|
||||
self, KubectlClient, "Kubectl Vulnerable To CVE-2019-11246", category=RemoteCodeExec, vid="KHV027",
|
||||
self,
|
||||
KubectlClient,
|
||||
"Kubectl Vulnerable To CVE-2019-11246",
|
||||
category=RemoteCodeExec,
|
||||
vid="KHV027",
|
||||
)
|
||||
self.binary_version = binary_version
|
||||
self.evidence = "kubectl version: {}".format(self.binary_version)
|
||||
@@ -101,7 +113,11 @@ class KubectlCpVulnerability(Vulnerability, Event):
|
||||
|
||||
def __init__(self, binary_version):
|
||||
Vulnerability.__init__(
|
||||
self, KubectlClient, "Kubectl Vulnerable To CVE-2019-1002101", category=RemoteCodeExec, vid="KHV028",
|
||||
self,
|
||||
KubectlClient,
|
||||
"Kubectl Vulnerable To CVE-2019-1002101",
|
||||
category=RemoteCodeExec,
|
||||
vid="KHV028",
|
||||
)
|
||||
self.binary_version = binary_version
|
||||
self.evidence = "kubectl version: {}".format(self.binary_version)
|
||||
|
||||
@@ -16,7 +16,11 @@ class DashboardExposed(Vulnerability, Event):
|
||||
|
||||
def __init__(self, nodes):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, "Dashboard Exposed", category=RemoteCodeExec, vid="KHV029",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
"Dashboard Exposed",
|
||||
category=RemoteCodeExec,
|
||||
vid="KHV029",
|
||||
)
|
||||
self.evidence = "nodes: {}".format(" ".join(nodes)) if nodes else None
|
||||
|
||||
|
||||
@@ -18,7 +18,11 @@ class PossibleDnsSpoofing(Vulnerability, Event):
|
||||
|
||||
def __init__(self, kubedns_pod_ip):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, "Possible DNS Spoof", category=IdentityTheft, vid="KHV030",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
"Possible DNS Spoof",
|
||||
category=IdentityTheft,
|
||||
vid="KHV030",
|
||||
)
|
||||
self.kubedns_pod_ip = kubedns_pod_ip
|
||||
self.evidence = "kube-dns at: {}".format(self.kubedns_pod_ip)
|
||||
@@ -61,7 +65,9 @@ class DnsSpoofHunter(ActiveHunter):
|
||||
self_ip = dns_info_res[IP].dst
|
||||
|
||||
arp_responses, _ = srp(
|
||||
Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(op=1, pdst=f"{self_ip}/24"), timeout=config.network_timeout, verbose=0,
|
||||
Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(op=1, pdst=f"{self_ip}/24"),
|
||||
timeout=config.network_timeout,
|
||||
verbose=0,
|
||||
)
|
||||
for _, response in arp_responses:
|
||||
if response[Ether].src == kubedns_pod_mac:
|
||||
|
||||
@@ -26,7 +26,11 @@ class EtcdRemoteWriteAccessEvent(Vulnerability, Event):
|
||||
|
||||
def __init__(self, write_res):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Etcd Remote Write Access Event", category=RemoteCodeExec, vid="KHV031",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Etcd Remote Write Access Event",
|
||||
category=RemoteCodeExec,
|
||||
vid="KHV031",
|
||||
)
|
||||
self.evidence = write_res
|
||||
|
||||
@@ -36,7 +40,11 @@ class EtcdRemoteReadAccessEvent(Vulnerability, Event):
|
||||
|
||||
def __init__(self, keys):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, name="Etcd Remote Read Access Event", category=AccessRisk, vid="KHV032",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
name="Etcd Remote Read Access Event",
|
||||
category=AccessRisk,
|
||||
vid="KHV032",
|
||||
)
|
||||
self.evidence = keys
|
||||
|
||||
@@ -149,7 +157,9 @@ class EtcdRemoteAccess(Hunter):
|
||||
logger.debug(f"Trying to access etcd insecurely at {self.event.host}")
|
||||
try:
|
||||
r = requests.get(
|
||||
f"http://{self.event.host}:{ETCD_PORT}/version", verify=False, timeout=config.network_timeout,
|
||||
f"http://{self.event.host}:{ETCD_PORT}/version",
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
)
|
||||
return r.content if r.status_code == 200 and r.content else False
|
||||
except requests.exceptions.ConnectionError:
|
||||
|
||||
@@ -35,7 +35,10 @@ class ExposedPodsHandler(Vulnerability, Event):
|
||||
|
||||
def __init__(self, pods):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Exposed Pods", category=InformationDisclosure,
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Exposed Pods",
|
||||
category=InformationDisclosure,
|
||||
)
|
||||
self.pods = pods
|
||||
self.evidence = f"count: {len(self.pods)}"
|
||||
@@ -47,7 +50,11 @@ class AnonymousAuthEnabled(Vulnerability, Event):
|
||||
|
||||
def __init__(self):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Anonymous Authentication", category=RemoteCodeExec, vid="KHV036",
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Anonymous Authentication",
|
||||
category=RemoteCodeExec,
|
||||
vid="KHV036",
|
||||
)
|
||||
|
||||
|
||||
@@ -56,7 +63,11 @@ class ExposedContainerLogsHandler(Vulnerability, Event):
|
||||
|
||||
def __init__(self):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Exposed Container Logs", category=InformationDisclosure, vid="KHV037",
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Exposed Container Logs",
|
||||
category=InformationDisclosure,
|
||||
vid="KHV037",
|
||||
)
|
||||
|
||||
|
||||
@@ -66,7 +77,11 @@ class ExposedRunningPodsHandler(Vulnerability, Event):
|
||||
|
||||
def __init__(self, count):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Exposed Running Pods", category=InformationDisclosure, vid="KHV038",
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Exposed Running Pods",
|
||||
category=InformationDisclosure,
|
||||
vid="KHV038",
|
||||
)
|
||||
self.count = count
|
||||
self.evidence = "{} running pods".format(self.count)
|
||||
@@ -77,7 +92,11 @@ class ExposedExecHandler(Vulnerability, Event):
|
||||
|
||||
def __init__(self):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Exposed Exec On Container", category=RemoteCodeExec, vid="KHV039",
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Exposed Exec On Container",
|
||||
category=RemoteCodeExec,
|
||||
vid="KHV039",
|
||||
)
|
||||
|
||||
|
||||
@@ -86,7 +105,11 @@ class ExposedRunHandler(Vulnerability, Event):
|
||||
|
||||
def __init__(self):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Exposed Run Inside Container", category=RemoteCodeExec, vid="KHV040",
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Exposed Run Inside Container",
|
||||
category=RemoteCodeExec,
|
||||
vid="KHV040",
|
||||
)
|
||||
|
||||
|
||||
@@ -95,7 +118,11 @@ class ExposedPortForwardHandler(Vulnerability, Event):
|
||||
|
||||
def __init__(self):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Exposed Port Forward", category=RemoteCodeExec, vid="KHV041",
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Exposed Port Forward",
|
||||
category=RemoteCodeExec,
|
||||
vid="KHV041",
|
||||
)
|
||||
|
||||
|
||||
@@ -105,7 +132,11 @@ class ExposedAttachHandler(Vulnerability, Event):
|
||||
|
||||
def __init__(self):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Exposed Attaching To Container", category=RemoteCodeExec, vid="KHV042",
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Exposed Attaching To Container",
|
||||
category=RemoteCodeExec,
|
||||
vid="KHV042",
|
||||
)
|
||||
|
||||
|
||||
@@ -115,7 +146,11 @@ class ExposedHealthzHandler(Vulnerability, Event):
|
||||
|
||||
def __init__(self, status):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Cluster Health Disclosure", category=InformationDisclosure, vid="KHV043",
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Cluster Health Disclosure",
|
||||
category=InformationDisclosure,
|
||||
vid="KHV043",
|
||||
)
|
||||
self.status = status
|
||||
self.evidence = f"status: {self.status}"
|
||||
@@ -143,7 +178,11 @@ class PrivilegedContainers(Vulnerability, Event):
|
||||
|
||||
def __init__(self, containers):
|
||||
Vulnerability.__init__(
|
||||
self, component=KubernetesCluster, name="Privileged Container", category=AccessRisk, vid="KHV044",
|
||||
self,
|
||||
component=KubernetesCluster,
|
||||
name="Privileged Container",
|
||||
category=AccessRisk,
|
||||
vid="KHV044",
|
||||
)
|
||||
self.containers = containers
|
||||
self.evidence = f"pod: {containers[0][0]}, " f"container: {containers[0][1]}, " f"count: {len(containers)}"
|
||||
@@ -154,7 +193,11 @@ class ExposedSystemLogs(Vulnerability, Event):
|
||||
|
||||
def __init__(self):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Exposed System Logs", category=InformationDisclosure, vid="KHV045",
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Exposed System Logs",
|
||||
category=InformationDisclosure,
|
||||
vid="KHV045",
|
||||
)
|
||||
|
||||
|
||||
@@ -163,7 +206,11 @@ class ExposedKubeletCmdline(Vulnerability, Event):
|
||||
|
||||
def __init__(self, cmdline):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Exposed Kubelet Cmdline", category=InformationDisclosure, vid="KHV046",
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Exposed Kubelet Cmdline",
|
||||
category=InformationDisclosure,
|
||||
vid="KHV046",
|
||||
)
|
||||
self.cmdline = cmdline
|
||||
self.evidence = f"cmdline: {self.cmdline}"
|
||||
@@ -270,7 +317,9 @@ class SecureKubeletPortHunter(Hunter):
|
||||
def test_container_logs(self):
|
||||
config = get_config()
|
||||
logs_url = self.path + KubeletHandlers.CONTAINERLOGS.value.format(
|
||||
pod_namespace=self.pod["namespace"], pod_id=self.pod["name"], container_name=self.pod["container"],
|
||||
pod_namespace=self.pod["namespace"],
|
||||
pod_id=self.pod["name"],
|
||||
container_name=self.pod["container"],
|
||||
)
|
||||
return self.session.get(logs_url, verify=False, timeout=config.network_timeout).status_code == 200
|
||||
|
||||
@@ -288,7 +337,11 @@ class SecureKubeletPortHunter(Hunter):
|
||||
return (
|
||||
"/cri/exec/"
|
||||
in self.session.get(
|
||||
exec_url, headers=headers, allow_redirects=False, verify=False, timeout=config.network_timeout,
|
||||
exec_url,
|
||||
headers=headers,
|
||||
allow_redirects=False,
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
).text
|
||||
)
|
||||
|
||||
@@ -303,10 +356,16 @@ class SecureKubeletPortHunter(Hunter):
|
||||
"Sec-Websocket-Protocol": "SPDY",
|
||||
}
|
||||
pf_url = self.path + KubeletHandlers.PORTFORWARD.value.format(
|
||||
pod_namespace=self.pod["namespace"], pod_id=self.pod["name"], port=80,
|
||||
pod_namespace=self.pod["namespace"],
|
||||
pod_id=self.pod["name"],
|
||||
port=80,
|
||||
)
|
||||
self.session.get(
|
||||
pf_url, headers=headers, verify=False, stream=True, timeout=config.network_timeout,
|
||||
pf_url,
|
||||
headers=headers,
|
||||
verify=False,
|
||||
stream=True,
|
||||
timeout=config.network_timeout,
|
||||
).status_code == 200
|
||||
# TODO: what to return?
|
||||
|
||||
@@ -314,7 +373,10 @@ class SecureKubeletPortHunter(Hunter):
|
||||
def test_run_container(self):
|
||||
config = get_config()
|
||||
run_url = self.path + KubeletHandlers.RUN.value.format(
|
||||
pod_namespace="test", pod_id="test", container_name="test", cmd="",
|
||||
pod_namespace="test",
|
||||
pod_id="test",
|
||||
container_name="test",
|
||||
cmd="",
|
||||
)
|
||||
# if we get a Method Not Allowed, we know we passed Authentication and Authorization.
|
||||
return self.session.get(run_url, verify=False, timeout=config.network_timeout).status_code == 405
|
||||
@@ -339,7 +401,10 @@ class SecureKubeletPortHunter(Hunter):
|
||||
return (
|
||||
"/cri/attach/"
|
||||
in self.session.get(
|
||||
attach_url, allow_redirects=False, verify=False, timeout=config.network_timeout,
|
||||
attach_url,
|
||||
allow_redirects=False,
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
).text
|
||||
)
|
||||
|
||||
@@ -347,7 +412,8 @@ class SecureKubeletPortHunter(Hunter):
|
||||
def test_logs_endpoint(self):
|
||||
config = get_config()
|
||||
logs_url = self.session.get(
|
||||
self.path + KubeletHandlers.LOGS.value.format(path=""), timeout=config.network_timeout,
|
||||
self.path + KubeletHandlers.LOGS.value.format(path=""),
|
||||
timeout=config.network_timeout,
|
||||
).text
|
||||
return "<pre>" in logs_url
|
||||
|
||||
@@ -355,7 +421,9 @@ class SecureKubeletPortHunter(Hunter):
|
||||
def test_pprof_cmdline(self):
|
||||
config = get_config()
|
||||
cmd = self.session.get(
|
||||
self.path + KubeletHandlers.PPROF_CMDLINE.value, verify=False, timeout=config.network_timeout,
|
||||
self.path + KubeletHandlers.PPROF_CMDLINE.value,
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
)
|
||||
return cmd.text if cmd.status_code == 200 else None
|
||||
|
||||
@@ -647,7 +715,10 @@ class MaliciousIntentViaSecureKubeletPort(ActiveHunter):
|
||||
)
|
||||
|
||||
self.rmdir_command(
|
||||
run_request_url, directory_created, number_of_rmdir_attempts, seconds_to_wait_for_os_command,
|
||||
run_request_url,
|
||||
directory_created,
|
||||
number_of_rmdir_attempts,
|
||||
seconds_to_wait_for_os_command,
|
||||
)
|
||||
|
||||
def check_file_exists(self, run_request_url, file):
|
||||
@@ -718,7 +789,11 @@ class MaliciousIntentViaSecureKubeletPort(ActiveHunter):
|
||||
return ProveAnonymousAuth.has_no_error_nor_exception(directory_exists)
|
||||
|
||||
def rmdir_command(
|
||||
self, run_request_url, directory_to_remove, number_of_rmdir_attempts, seconds_to_wait_for_os_command,
|
||||
self,
|
||||
run_request_url,
|
||||
directory_to_remove,
|
||||
number_of_rmdir_attempts,
|
||||
seconds_to_wait_for_os_command,
|
||||
):
|
||||
if self.check_directory_exists(run_request_url, directory_to_remove):
|
||||
for _ in range(number_of_rmdir_attempts):
|
||||
@@ -985,13 +1060,17 @@ class ProveRunHandler(ActiveHunter):
|
||||
cmd=command,
|
||||
)
|
||||
return self.event.session.post(
|
||||
f"{self.base_path}/{run_url}", verify=False, timeout=config.network_timeout,
|
||||
f"{self.base_path}/{run_url}",
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
).text
|
||||
|
||||
def execute(self):
|
||||
config = get_config()
|
||||
r = self.event.session.get(
|
||||
f"{self.base_path}/" + KubeletHandlers.PODS.value, verify=False, timeout=config.network_timeout,
|
||||
f"{self.base_path}/" + KubeletHandlers.PODS.value,
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
)
|
||||
if "items" in r.text:
|
||||
pods_data = r.json()["items"]
|
||||
@@ -1025,7 +1104,9 @@ class ProveContainerLogsHandler(ActiveHunter):
|
||||
def execute(self):
|
||||
config = get_config()
|
||||
pods_raw = self.event.session.get(
|
||||
self.base_url + KubeletHandlers.PODS.value, verify=False, timeout=config.network_timeout,
|
||||
self.base_url + KubeletHandlers.PODS.value,
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
).text
|
||||
if "items" in pods_raw:
|
||||
pods_data = json.loads(pods_raw)["items"]
|
||||
|
||||
@@ -25,7 +25,11 @@ class WriteMountToVarLog(Vulnerability, Event):
|
||||
|
||||
def __init__(self, pods):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, "Pod With Mount To /var/log", category=PrivilegeEscalation, vid="KHV047",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
"Pod With Mount To /var/log",
|
||||
category=PrivilegeEscalation,
|
||||
vid="KHV047",
|
||||
)
|
||||
self.pods = pods
|
||||
self.evidence = "pods: {}".format(", ".join((pod["metadata"]["name"] for pod in self.pods)))
|
||||
@@ -37,7 +41,10 @@ class DirectoryTraversalWithKubelet(Vulnerability, Event):
|
||||
|
||||
def __init__(self, output):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, "Root Traversal Read On The Kubelet", category=PrivilegeEscalation,
|
||||
self,
|
||||
KubernetesCluster,
|
||||
"Root Traversal Read On The Kubelet",
|
||||
category=PrivilegeEscalation,
|
||||
)
|
||||
self.output = output
|
||||
self.evidence = "output: {}".format(self.output)
|
||||
@@ -82,7 +89,10 @@ class ProveVarLogMount(ActiveHunter):
|
||||
|
||||
def run(self, command, container):
|
||||
run_url = KubeletHandlers.RUN.value.format(
|
||||
podNamespace=container["namespace"], podID=container["pod"], containerName=container["name"], cmd=command,
|
||||
podNamespace=container["namespace"],
|
||||
podID=container["pod"],
|
||||
containerName=container["name"],
|
||||
cmd=command,
|
||||
)
|
||||
return self.event.session.post(f"{self.base_path}/{run_url}", verify=False).text
|
||||
|
||||
@@ -91,7 +101,9 @@ class ProveVarLogMount(ActiveHunter):
|
||||
config = get_config()
|
||||
logger.debug("accessing /pods manually on ProveVarLogMount")
|
||||
pods = self.event.session.get(
|
||||
f"{self.base_path}/" + KubeletHandlers.PODS.value, verify=False, timeout=config.network_timeout,
|
||||
f"{self.base_path}/" + KubeletHandlers.PODS.value,
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
).json()["items"]
|
||||
for pod in pods:
|
||||
volume = VarLogMountHunter(ExposedPodsHandler(pods=pods)).has_write_mount_to(pod, "/var/log")
|
||||
@@ -117,7 +129,9 @@ class ProveVarLogMount(ActiveHunter):
|
||||
path=re.sub(r"^/var/log", "", host_path) + symlink_name
|
||||
)
|
||||
content = self.event.session.get(
|
||||
f"{self.base_path}/{path_in_logs_endpoint}", verify=False, timeout=config.network_timeout,
|
||||
f"{self.base_path}/{path_in_logs_endpoint}",
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
).text
|
||||
# removing symlink
|
||||
self.run(f"rm {mount_path}/{symlink_name}", container=container)
|
||||
@@ -134,7 +148,10 @@ class ProveVarLogMount(ActiveHunter):
|
||||
}
|
||||
try:
|
||||
output = self.traverse_read(
|
||||
"/etc/shadow", container=cont, mount_path=mount_path, host_path=volume["hostPath"]["path"],
|
||||
"/etc/shadow",
|
||||
container=cont,
|
||||
mount_path=mount_path,
|
||||
host_path=volume["hostPath"]["path"],
|
||||
)
|
||||
self.publish_event(DirectoryTraversalWithKubelet(output=output))
|
||||
except Exception:
|
||||
|
||||
@@ -23,7 +23,11 @@ class KubeProxyExposed(Vulnerability, Event):
|
||||
|
||||
def __init__(self):
|
||||
Vulnerability.__init__(
|
||||
self, KubernetesCluster, "Proxy Exposed", category=InformationDisclosure, vid="KHV049",
|
||||
self,
|
||||
KubernetesCluster,
|
||||
"Proxy Exposed",
|
||||
category=InformationDisclosure,
|
||||
vid="KHV049",
|
||||
)
|
||||
|
||||
|
||||
@@ -89,7 +93,9 @@ class ProveProxyExposed(ActiveHunter):
|
||||
def execute(self):
|
||||
config = get_config()
|
||||
version_metadata = requests.get(
|
||||
f"http://{self.event.host}:{self.event.port}/version", verify=False, timeout=config.network_timeout,
|
||||
f"http://{self.event.host}:{self.event.port}/version",
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
).json()
|
||||
if "buildDate" in version_metadata:
|
||||
self.event.evidence = "build date: {}".format(version_metadata["buildDate"])
|
||||
@@ -107,11 +113,15 @@ class K8sVersionDisclosureProve(ActiveHunter):
|
||||
def execute(self):
|
||||
config = get_config()
|
||||
version_metadata = requests.get(
|
||||
f"http://{self.event.host}:{self.event.port}/version", verify=False, timeout=config.network_timeout,
|
||||
f"http://{self.event.host}:{self.event.port}/version",
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
).json()
|
||||
if "gitVersion" in version_metadata:
|
||||
self.publish_event(
|
||||
K8sVersionDisclosure(
|
||||
version=version_metadata["gitVersion"], from_endpoint="/version", extra_info="on kube-proxy",
|
||||
version=version_metadata["gitVersion"],
|
||||
from_endpoint="/version",
|
||||
extra_info="on kube-proxy",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -28,7 +28,10 @@ class SecretsAccess(Vulnerability, Event):
|
||||
|
||||
def __init__(self, evidence):
|
||||
Vulnerability.__init__(
|
||||
self, component=KubernetesCluster, name="Access to pod's secrets", category=AccessRisk,
|
||||
self,
|
||||
component=KubernetesCluster,
|
||||
name="Access to pod's secrets",
|
||||
category=AccessRisk,
|
||||
)
|
||||
self.evidence = evidence
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@ class HTTPDispatcher:
|
||||
dispatch_url = os.environ.get("KUBEHUNTER_HTTP_DISPATCH_URL", "https://localhost/")
|
||||
try:
|
||||
r = requests.request(
|
||||
dispatch_method, dispatch_url, json=report, headers={"Content-Type": "application/json"},
|
||||
dispatch_method,
|
||||
dispatch_url,
|
||||
json=report,
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
r.raise_for_status()
|
||||
logger.info(f"Report was dispatched to: {dispatch_url}")
|
||||
|
||||
@@ -8,7 +8,7 @@ set_config(Config())
|
||||
|
||||
|
||||
def test_presetcloud():
|
||||
""" Testing if it doesn't try to run get_cloud if the cloud type is already set.
|
||||
"""Testing if it doesn't try to run get_cloud if the cloud type is already set.
|
||||
get_cloud(1.2.3.4) will result with an error
|
||||
"""
|
||||
expcted = "AWS"
|
||||
|
||||
@@ -20,7 +20,9 @@ def test_ApiServer():
|
||||
m.get("https://mockOther:443", text="elephant")
|
||||
m.get("https://mockKubernetes:443", text='{"code":403}', status_code=403)
|
||||
m.get(
|
||||
"https://mockKubernetes:443/version", text='{"major": "1.14.10"}', status_code=200,
|
||||
"https://mockKubernetes:443/version",
|
||||
text='{"major": "1.14.10"}',
|
||||
status_code=200,
|
||||
)
|
||||
|
||||
e = Event()
|
||||
@@ -44,11 +46,15 @@ def test_ApiServerWithServiceAccountToken():
|
||||
counter = 0
|
||||
with requests_mock.Mocker() as m:
|
||||
m.get(
|
||||
"https://mockKubernetes:443", request_headers={"Authorization": "Bearer very_secret"}, text='{"code":200}',
|
||||
"https://mockKubernetes:443",
|
||||
request_headers={"Authorization": "Bearer very_secret"},
|
||||
text='{"code":200}',
|
||||
)
|
||||
m.get("https://mockKubernetes:443", text='{"code":403}', status_code=403)
|
||||
m.get(
|
||||
"https://mockKubernetes:443/version", text='{"major": "1.14.10"}', status_code=200,
|
||||
"https://mockKubernetes:443/version",
|
||||
text='{"major": "1.14.10"}',
|
||||
status_code=200,
|
||||
)
|
||||
m.get("https://mockOther:443", text="elephant")
|
||||
|
||||
|
||||
@@ -56,7 +56,8 @@ def test_AccessApiServer():
|
||||
with requests_mock.Mocker() as m:
|
||||
m.get("https://mockKubernetes:443/api", text="{}")
|
||||
m.get(
|
||||
"https://mockKubernetes:443/api/v1/namespaces", text='{"items":[{"metadata":{"name":"hello"}}]}',
|
||||
"https://mockKubernetes:443/api/v1/namespaces",
|
||||
text='{"items":[{"metadata":{"name":"hello"}}]}',
|
||||
)
|
||||
m.get(
|
||||
"https://mockKubernetes:443/api/v1/pods",
|
||||
@@ -64,10 +65,12 @@ def test_AccessApiServer():
|
||||
{"metadata":{"name":"podB", "namespace":"namespaceB"}}]}',
|
||||
)
|
||||
m.get(
|
||||
"https://mockkubernetes:443/apis/rbac.authorization.k8s.io/v1/roles", status_code=403,
|
||||
"https://mockkubernetes:443/apis/rbac.authorization.k8s.io/v1/roles",
|
||||
status_code=403,
|
||||
)
|
||||
m.get(
|
||||
"https://mockkubernetes:443/apis/rbac.authorization.k8s.io/v1/clusterroles", text='{"items":[]}',
|
||||
"https://mockkubernetes:443/apis/rbac.authorization.k8s.io/v1/clusterroles",
|
||||
text='{"items":[]}',
|
||||
)
|
||||
m.get(
|
||||
"https://mockkubernetes:443/version",
|
||||
@@ -91,7 +94,8 @@ def test_AccessApiServer():
|
||||
# TODO check that these responses reflect what Kubernetes does
|
||||
m.get("https://mocktoken:443/api", text="{}")
|
||||
m.get(
|
||||
"https://mocktoken:443/api/v1/namespaces", text='{"items":[{"metadata":{"name":"hello"}}]}',
|
||||
"https://mocktoken:443/api/v1/namespaces",
|
||||
text='{"items":[{"metadata":{"name":"hello"}}]}',
|
||||
)
|
||||
m.get(
|
||||
"https://mocktoken:443/api/v1/pods",
|
||||
@@ -99,7 +103,8 @@ def test_AccessApiServer():
|
||||
{"metadata":{"name":"podB", "namespace":"namespaceB"}}]}',
|
||||
)
|
||||
m.get(
|
||||
"https://mocktoken:443/apis/rbac.authorization.k8s.io/v1/roles", status_code=403,
|
||||
"https://mocktoken:443/apis/rbac.authorization.k8s.io/v1/roles",
|
||||
status_code=403,
|
||||
)
|
||||
m.get(
|
||||
"https://mocktoken:443/apis/rbac.authorization.k8s.io/v1/clusterroles",
|
||||
@@ -228,10 +233,12 @@ def test_AccessApiServerActive():
|
||||
)
|
||||
m.post("https://mockKubernetes:443/api/v1/clusterroles", text="{}")
|
||||
m.post(
|
||||
"https://mockkubernetes:443/apis/rbac.authorization.k8s.io/v1/clusterroles", text="{}",
|
||||
"https://mockkubernetes:443/apis/rbac.authorization.k8s.io/v1/clusterroles",
|
||||
text="{}",
|
||||
)
|
||||
m.post(
|
||||
"https://mockkubernetes:443/api/v1/namespaces/hello-namespace/pods", text="{}",
|
||||
"https://mockkubernetes:443/api/v1/namespaces/hello-namespace/pods",
|
||||
text="{}",
|
||||
)
|
||||
m.post(
|
||||
"https://mockkubernetes:443" "/apis/rbac.authorization.k8s.io/v1/namespaces/hello-namespace/roles",
|
||||
|
||||
@@ -73,8 +73,8 @@ def create_test_event_type_one():
|
||||
|
||||
|
||||
def create_test_event_type_two():
|
||||
exposed_existing_privileged_containers_via_secure_kubelet_port_event = ExposedExistingPrivilegedContainersViaSecureKubeletPort(
|
||||
exposed_privileged_containers
|
||||
exposed_existing_privileged_containers_via_secure_kubelet_port_event = (
|
||||
ExposedExistingPrivilegedContainersViaSecureKubeletPort(exposed_privileged_containers)
|
||||
)
|
||||
exposed_existing_privileged_containers_via_secure_kubelet_port_event.host = "localhost"
|
||||
exposed_existing_privileged_containers_via_secure_kubelet_port_event.session = requests.Session()
|
||||
|
||||
Reference in New Issue
Block a user