22import argparse
33import re
44import collections
5+ from sqlalchemy import true
56from termcolor import colored
67
78"""
8- Script to generate a representations from information leaked from an instance
9- of Kubernetes Metrics Server via its endpoints "/metrics".
9+ Script to generate a representations from information leaked from an instance of Kubernetes Metrics Server via its endpoints "/metrics".
1010
11- Also support output from an instance of Prometheus.
11+ Support also the output from an instance of an exporter for Prometheus that is using the Prometheus data format .
1212
1313Command to get the data:
1414
2929Dependencies:
3030 None
3131
32- References :
32+ Resources :
3333 https://github.com/projectdiscovery/nuclei-templates/blob/main/http/technologies/kubernetes/kubelet/kubelet-metrics.yaml
3434 https://github.com/projectdiscovery/nuclei-templates/blob/main/http/exposures/configs/prometheus-metrics.yaml
3535"""
36- K8S_RESOURCE_TYPES = ["pod" , "deployment" , "service" , "configmap" , "node" , "secret" ]
37- K8S_OTHERS_TYPES = ["controller" , "host" , "webhook" ]
36+ K8S_RESOURCE_TYPES = ["pod" , "deployment" , "service" , "configmap" , "node" , "secret" , "replicaset" , "daemonset" , "networkpolicy" , "statefulset" ]
37+ K8S_OTHERS_TYPES = ["controller" , "host" , "webhook" , "lease" , "pod_cidr" ]
38+ K8S_RESOURCE_TYPES .sort ()
39+ K8S_OTHERS_TYPES .sort ()
3840NAMESPACE_REGEX = r'namespace="(.*?)"'
3941RESOURCE_KEY = "resources"
4042OTHER_KEY = "others"
4143MISC_KEY = "misc"
4244
4345
46+ def is_name_not_none (name ):
47+ return (name .lower ().strip (" \t " ) != "<none>" )
48+
49+
4450def load_records (kms_data_file ):
4551 # KEY is the namespace
4652 # VALUE is a DICT for which KEY is the K8S resource type and VALUE is a list of the related K8S resource/component names
4753 records = {}
4854 with open (kms_data_file , mode = "r" , encoding = "utf-8" ) as f :
4955 lines = f .read ().splitlines ()
56+ lines_count = len (lines )
57+ line_id = 0
5058 for line in lines :
59+ line_id += 1
60+ loading_progress = round ((line_id / lines_count ) * 100 )
61+ print (f"\r Extracting data: { str (loading_progress ).zfill (2 )} %" , end = "" , flush = True )
5162 # Skip comment
5263 if line .startswith ("#" ):
5364 continue
@@ -64,31 +75,35 @@ def load_records(kms_data_file):
6475 results = re .findall (resource_type_regex , line , re .IGNORECASE )
6576 if len (results ) > 0 :
6677 resource_name = results [0 ]
67- if resource_type not in records [k8s_ns ][RESOURCE_KEY ]:
68- records [k8s_ns ][RESOURCE_KEY ][resource_type ] = []
69- if resource_name not in records [k8s_ns ][RESOURCE_KEY ][resource_type ]:
70- records [k8s_ns ][RESOURCE_KEY ][resource_type ].append (resource_name )
78+ if is_name_not_none (resource_name ):
79+ if resource_type not in records [k8s_ns ][RESOURCE_KEY ]:
80+ records [k8s_ns ][RESOURCE_KEY ][resource_type ] = []
81+ if resource_name not in records [k8s_ns ][RESOURCE_KEY ][resource_type ]:
82+ records [k8s_ns ][RESOURCE_KEY ][resource_type ].append (resource_name )
7183 # Extract the other types of components
7284 for component_type in K8S_OTHERS_TYPES :
7385 component_type_regex = f"{ component_type } =\" (.*?)\" "
7486 results = re .findall (component_type_regex , line , re .IGNORECASE )
7587 if len (results ) > 0 :
7688 component_name = results [0 ]
77- if component_type not in records [k8s_ns ][OTHER_KEY ]:
78- records [k8s_ns ][OTHER_KEY ][component_type ] = []
79- if component_name not in records [k8s_ns ][OTHER_KEY ][component_type ]:
80- records [k8s_ns ][OTHER_KEY ][component_type ].append (component_name )
89+ if is_name_not_none (component_name ):
90+ if component_type not in records [k8s_ns ][OTHER_KEY ]:
91+ records [k8s_ns ][OTHER_KEY ][component_type ] = []
92+ if component_name not in records [k8s_ns ][OTHER_KEY ][component_type ]:
93+ records [k8s_ns ][OTHER_KEY ][component_type ].append (component_name )
8194 # Extract special item like this one:
8295 # gotk_reconcile_duration_seconds_bucket{kind="Kustomization",name="workspace",namespace="kommander",le="2"}
83- if ' kind=' in line and ' name=' in line :
96+ if " kind=" in line and " name=" in line :
8497 kind_regex = "kind=\" (.*?)\" "
8598 kind_name = re .findall (kind_regex , line , re .IGNORECASE )[0 ]
86- name_regex = "name=\" (.*?)\" "
87- name_name = re .findall (name_regex , line , re .IGNORECASE )[0 ]
88- if kind_name not in records [k8s_ns ][MISC_KEY ]:
89- records [k8s_ns ][MISC_KEY ][kind_name ] = []
90- if name_name not in records [k8s_ns ][MISC_KEY ][kind_name ]:
91- records [k8s_ns ][MISC_KEY ][kind_name ].append (name_name )
99+ if kind_name .lower () not in (K8S_RESOURCE_TYPES + K8S_OTHERS_TYPES ) and is_name_not_none (kind_name ):
100+ name_regex = "name=\" (.*?)\" "
101+ name_name = re .findall (name_regex , line , re .IGNORECASE )[0 ]
102+ if kind_name not in records [k8s_ns ][MISC_KEY ]:
103+ records [k8s_ns ][MISC_KEY ][kind_name ] = []
104+ if name_name not in records [k8s_ns ][MISC_KEY ][kind_name ]:
105+ records [k8s_ns ][MISC_KEY ][kind_name ].append (name_name )
106+ print ("\r " , end = "" , flush = True )
92107 return collections .OrderedDict (sorted (records .items ()))
93108
94109
@@ -100,12 +115,12 @@ def generate_tree_representation(records):
100115 for category in [RESOURCE_KEY , OTHER_KEY , MISC_KEY ]:
101116 category_items = namespace_items [category ]
102117 if len (category_items ) > 0 :
103- print (colored (f"=> { category .capitalize ()} " , "light_cyan " ))
118+ print (colored (f"=> { category .capitalize ()} " , "light_magenta " ))
104119 for category_item_type in category_items :
105120 item_collection = category_items [category_item_type ]
106121 item_count = len (item_collection )
107122 item_collection .sort ()
108- print (colored (f"==> { category_item_type .capitalize ()} ({ item_count } ): " , "light_magenta " ), end = "" )
123+ print (colored (f"==> { category_item_type .capitalize ()} ({ item_count } ): " , "light_cyan " ), end = "" )
109124 print (", " .join (item_collection ))
110125
111126
0 commit comments