Check all running images's vulnerability & size running in k8s cluster

Collect the list of images
mkdir -p /tmp/k8s-image-list/
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{"\n"}{.metadata.namespace}{"\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}' |sort | uniq -c | awk '{print $2, $3, $4}' | tr "," " done" > /tmp/k8s-image-list/k8s-images-details.txt
Segregate the images
import os
def process_dc_image_txt_files(directory):
unique_images=set()
files = os.listdir(directory)
txt_files = [file for file in files if file.endswith('.txt')]
for txt_file in txt_files:
input_file = os.path.join(directory, txt_file)
print("Fetch for: ",input_file)
# namespace = collect_unique_ns(input_file)
# # print("Unique Namespaces:")
# # print("------------------")
# # for ns in set(namespace):
# # print(ns)
# # print()
images = collect_images(input_file)
unique_images.update(images)
return unique_images
def collect_unique_ns(input_file):
all_words_except_first = []
# Read the file and collect all words except the first word
with open(input_file, 'r') as file:
for line in file:
words = line.strip().split()
if len(words) > 1:
all_words_except_first.extend(words[0:1])
return all_words_except_first
def collect_images(input_file):
all_words_except_first = []
# Read the file and collect all words except the first word
with open(input_file, 'r') as file:
for line in file:
words = line.strip().split()
if len(words) > 1:
all_words_except_first.extend(words[1:])
return all_words_except_first
def prepare_image_file(output_file, unique_images):
with open(output_file, 'w') as file:
for image in sorted(unique_images):
file.write(str(image) + '\n')
if __name__ == "__main__":
directory = "/tmp/k8s-image-list" # Replace with your directory path
unique_images=process_dc_image_txt_files(directory)
output_file = "images.txt"
prepare_image_file(output_file,unique_images)
print("done")
Scan Image List
#!/bin/bash
###########################################################################
# How to run ? bash scan_images.sh <images-list-file-full-path> <metrics-prefix> #
# example : bash scan_images.sh images.list demo #
# Author: Narendranath Panda #
###########################################################################
image_list_file=$1
image_list_file=${image_list_file:-images.txt}
scan_version=$(date +"%d-%m-%Y")
mkdir -p $scan_version
echo
#prom_file=$scan_version/image_scanning_result.prom
reports_dir=$scan_version/reports
mkdir -p $reports_dir
logs_dir=$scan_version/logs
mkdir -p $logs_dir
metrics_dir=$scan_version/metrics
mkdir -p $metrics_dir
echo "" >$logs_dir/failed-images.log
echo "" >$logs_dir/images-exists.log
echo "" >$logs_dir/processed-images.log
metric_prefix=$2
default_value="my"
metric_prefix=${metric_prefix:-$default_value}
number=0
if [ -e "$image_list_file" ]; then
echo "Scanning starts..."
for image_name in $(cat $image_list_file); do
((number++))
echo $number". "$image_name
image_report=($(echo $image_name | tr "/" _ | tr ":" _))
grype_file_name=$reports_dir/$image_report-grype.json
prom_file_name=$metrics_dir/$image_report.prom
if [ -e "$grype_file_name" ]; then
echo "$image_name" >>$logs_dir/images-exists.log
else
grype $image_name --quiet --add-cpes-if-none --output json >grype.out 2>&1
if [ $? -ne 0 ]; then
echo "$image_name" >>$logs_dir/failed-images.log
else
mv grype.out $grype_file_name
H_vul=$(cat $grype_file_name | grep -i High | wc -l)
L_vul=$(cat $grype_file_name | grep -i Low | wc -l)
M_vul=$(cat $grype_file_name | grep -i Medium | wc -l)
C_vul=$(cat $grype_file_name | grep -i Critical | wc -l)
N_vul=$(cat $grype_file_name | grep -i Negligible | wc -l)
digests=$(cat $grype_file_name | jq '.source.target.repoDigests[0]')
docker images $image_name --digests --format "{{json . }}" >$reports_dir/$image_report-docker.json
size=$(cat $reports_dir/$image_report-docker.json | jq -sc '.[] | {Repository, Size}' | jq -s 'map(. + {"originalSize": .Size})' | jq -r '
map(
if .Size | endswith("GB") then
.Size |= (gsub("GB"; "") | tonumber * 1024 * 1024 * 1024)
else
if .Size | endswith("MB") then
.Size |= (gsub("MB"; "") | tonumber * 1024 * 1024)
else
if .Size | endswith("KB") then
.Size |= (gsub("KB"; "") | tonumber * 1024)
else
.size | tonumber
end
end
end
)' | jq .[0].Size)
repository=$(cat $reports_dir/$image_report-docker.json | jq '.Repository')
image_registry=$(cat $reports_dir/$image_report-docker.json |jq -r '.Repository' | awk -F'/' '{print $1}' )
tag=$(cat $reports_dir/$image_report-docker.json | jq '.Tag')
IMAGE_ID=$(cat $reports_dir/$image_report-docker.json | jq -r ".ID")
echo "${metric_prefix}_image_info{image_digest=$digests,image_name=\"$image_name\",image_registry=\"$image_registry\",image_repository=$repository,image_tag=$tag} 1" >>$prom_file_name
echo "${metric_prefix}_image_vulnerability_severity_count{image_digest=$digests,severity=\"Critical\"} $C_vul" >>$prom_file_name
echo "${metric_prefix}_image_vulnerability_severity_count{image_digest=$digests,severity=\"High\"} $H_vul" >>$prom_file_name
echo "${metric_prefix}_image_vulnerability_severity_count{image_digest=$digests,severity=\"Medium\"} $M_vul" >>$prom_file_name
echo "${metric_prefix}_image_vulnerability_severity_count{image_digest=$digests,severity=\"Low\"} $L_vul" >>$prom_file_name
echo "${metric_prefix}_image_vulnerability_severity_count{image_digest=$digests,severity=\"Negligible\"} $N_vul" >>$prom_file_name
echo "${metric_prefix}_image_size_in_bytes{image_digest=$digests} $size" >>$prom_file_name
docker rmi $IMAGE_ID >/dev/null 2>&1
echo image_id=$IMAGE_ID, image_digest=$digests,image_name=\"$image_name\",image_registry=\"$image_registry\",image_repository=$repository,image_tag=$tag >>$logs_dir/processed-images.log
fi
fi
done
echo
echo
ls -lrt $metrics_dir
echo
echo
echo "Scanning done..."
echo
echo
echo "execute this command to copy to node_exporter/textfilecollector"
echo "cp 20-11-2023/metrics/*.prom /var/lib/node-exporter/textfile_collector/"
echo
else
echo "File '$image_list_file' does not exist in the directory.!"
fi
Visulaize the metrics
- Setting up Monitoring Stack in a Node (docker container)
- Node-exporter: http://localhost:9100/metrics
- Prometheus : http://localhost:9090/graph?g0.expr=my_image_info&g0.tab=1&g0.stacked=0&g0.show_exemplars=0&g0.range_input=1h
- Grafana : https://github.com/naren4b/nks/tree/main/apps/image-scanner