Skip to main content

Kubernetes resource hygiene scanner

Project description

Kubernetes Resource Leak Detector

A production-ready CLI that scans Kubernetes namespaces and reports resource hygiene risks. It prints a clean console report and can notify Slack and Microsoft Teams (Adaptive Card).

What it detects

Current checks:

  • Missing CPU/memory requests
  • Missing CPU/memory limits
  • Pods in CrashLoopBackOff
  • Containers using the latest tag
  • Images not pinned by digest
  • Security context: runAsNonRoot, readOnlyRootFilesystem, allowPrivilegeEscalation=false
  • Capabilities: must drop ALL
  • Seccomp profile: RuntimeDefault
  • Health probes: liveness/readiness/startup
  • PodDisruptionBudget missing for Deployments/StatefulSets
  • NetworkPolicies missing per namespace
  • Secret env usage (secretKeyRef / secretRef)

Manual review (recommended/roadmap):

  • Pod security standards alignment (baseline/restricted)
  • Image signing and vulnerability scan status
  • RBAC least privilege review (avoid cluster-admin, scope to namespaces)
  • Secrets management posture (CSI/External Secrets, rotation)
  • Scheduling safety: anti-affinity / topology spread where needed
  • Networking posture: default-deny + explicit allow where required
  • Disruption strategy for critical workloads (PDBs/SLOs)
  • RBAC: least-privilege service accounts, no cluster-admin
  • Secrets: avoid env var secrets, use CSI/External Secrets where possible

Workload coverage includes Deployments, StatefulSets, DaemonSets, Jobs, CronJobs, and standalone Pods.

See the remediation guide: docs/solutions.md.

Quick start (local)

python -m venv .venv
source .venv/bin/activate
pip install -e .

kube-leak-detector

Installation

From source (developer install):

python -m venv .venv
source .venv/bin/activate
pip install -e .

From a built wheel:

make build
pip install dist/*.whl

Container image:

podman build -t kube-leak-detector:local .

Usage

kube-leak-detector \
  --slack-webhook https://hooks.slack.com/services/... \
  --teams-webhook https://outlook.office.com/webhook/... \
  --color

Environment variables:

  • SLACK_WEBHOOK_URL
  • TEAMS_WEBHOOK_URL

Exit codes

  • 0 - no issues detected
  • 2 - issues detected

This behavior makes it CI-friendly. For local convenience, make run ignores non-zero exit codes.

Makefile shortcuts

  • make install - create venv and install the CLI in editable mode
  • make run - run with colored output (ignores non-zero exit code)
  • make run-ci - run with colored output (keeps exit code)

Release (PyPI)

  1. Bump the version in pyproject.toml.
  2. Build the dist:
make build
  1. Publish:
make publish

make publish uses twine and uploads to the default PyPI repository. Ensure credentials are configured in your environment.

Authentication

The CLI auto-detects auth:

  • In-cluster: uses the service account when KUBERNETES_SERVICE_HOST is set
  • Local: falls back to kubeconfig (~/.kube/config)

Notifications

  • Slack: simple text payload
  • Teams: Adaptive Card payload (suitable for Teams incoming webhook)

Output example

==============================
Kube Leak Detector Report
==============================

Summary
-------
- Missing requests..... 5
- Missing limits....... 8
- CrashLoopBackOff..... 0
- Uses latest tag...... 0

[Missing requests] (5)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  kube-system/kube-apiserver-kind-control-plane kube-apiserver        registry.k8s.io/kube-apiserver:v1.35.0

Full sample report

==============================
Kube Leak Detector Report
==============================

Summary
-------
- Missing requests.... 2
- Missing limits...... 3
- CrashLoopBackOff.... 0
- Uses latest tag..... 0
- Image not pinned.... 4
- runAsNonRoot missing 4
- readOnlyRootFS off.. 3
- Privilege escalation 3
- Caps not dropped.... 3
- Seccomp not default. 4
- Missing liveness.... 3
- Missing readiness... 3
- Missing startup..... 4
- PDB missing......... 2
- NetworkPolicy missing 2
- Secret env usage.... 0
- Manual review....... 3

[Missing requests] (2)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  local-path-storage/Deployment/local-path-... local-path-provisioner docker.io/kindest/local-path-provisioner:v20251212-v0...
  kube-system/DaemonSet/kube-proxy             kube-proxy             registry.k8s.io/kube-proxy:v1.35.0

[Missing limits] (3)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  kube-system/Deployment/coredns               coredns                registry.k8s.io/coredns/coredns:v1.13.1
  local-path-storage/Deployment/local-path-... local-path-provisioner docker.io/kindest/local-path-provisioner:v20251212-v0...
  kube-system/DaemonSet/kube-proxy             kube-proxy             registry.k8s.io/kube-proxy:v1.35.0

[CrashLoopBackOff] (0)
  No issues found

[Uses latest tag] (0)
  No issues found

[Image not pinned] (4)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  kube-system/Deployment/coredns               coredns                registry.k8s.io/coredns/coredns:v1.13.1
  local-path-storage/Deployment/local-path-... local-path-provisioner docker.io/kindest/local-path-provisioner:v20251212-v0...
  kube-system/DaemonSet/kindnet                kindnet-cni            docker.io/kindest/kindnetd:v20251212-v0.29.0-alpha-10...
  kube-system/DaemonSet/kube-proxy             kube-proxy             registry.k8s.io/kube-proxy:v1.35.0

[runAsNonRoot missing] (4)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  kube-system/Deployment/coredns               coredns                registry.k8s.io/coredns/coredns:v1.13.1
  local-path-storage/Deployment/local-path-... local-path-provisioner docker.io/kindest/local-path-provisioner:v20251212-v0...
  kube-system/DaemonSet/kindnet                kindnet-cni            docker.io/kindest/kindnetd:v20251212-v0.29.0-alpha-10...
  kube-system/DaemonSet/kube-proxy             kube-proxy             registry.k8s.io/kube-proxy:v1.35.0

[readOnlyRootFS off] (3)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  local-path-storage/Deployment/local-path-... local-path-provisioner docker.io/kindest/local-path-provisioner:v20251212-v0...
  kube-system/DaemonSet/kindnet                kindnet-cni            docker.io/kindest/kindnetd:v20251212-v0.29.0-alpha-10...
  kube-system/DaemonSet/kube-proxy             kube-proxy             registry.k8s.io/kube-proxy:v1.35.0

[Privilege escalation] (3)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  local-path-storage/Deployment/local-path-... local-path-provisioner docker.io/kindest/local-path-provisioner:v20251212-v0...
  kube-system/DaemonSet/kindnet                kindnet-cni            docker.io/kindest/kindnetd:v20251212-v0.29.0-alpha-10...
  kube-system/DaemonSet/kube-proxy             kube-proxy             registry.k8s.io/kube-proxy:v1.35.0

[Caps not dropped] (3)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  local-path-storage/Deployment/local-path-... local-path-provisioner docker.io/kindest/local-path-provisioner:v20251212-v0...
  kube-system/DaemonSet/kindnet                kindnet-cni            docker.io/kindest/kindnetd:v20251212-v0.29.0-alpha-10...
  kube-system/DaemonSet/kube-proxy             kube-proxy             registry.k8s.io/kube-proxy:v1.35.0

[Seccomp not default] (4)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  kube-system/Deployment/coredns               coredns                registry.k8s.io/coredns/coredns:v1.13.1
  local-path-storage/Deployment/local-path-... local-path-provisioner docker.io/kindest/local-path-provisioner:v20251212-v0...
  kube-system/DaemonSet/kindnet                kindnet-cni            docker.io/kindest/kindnetd:v20251212-v0.29.0-alpha-10...
  kube-system/DaemonSet/kube-proxy             kube-proxy             registry.k8s.io/kube-proxy:v1.35.0

[Missing liveness] (3)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  local-path-storage/Deployment/local-path-... local-path-provisioner docker.io/kindest/local-path-provisioner:v20251212-v0...
  kube-system/DaemonSet/kindnet                kindnet-cni            docker.io/kindest/kindnetd:v20251212-v0.29.0-alpha-10...
  kube-system/DaemonSet/kube-proxy             kube-proxy             registry.k8s.io/kube-proxy:v1.35.0

[Missing readiness] (3)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  local-path-storage/Deployment/local-path-... local-path-provisioner docker.io/kindest/local-path-provisioner:v20251212-v0...
  kube-system/DaemonSet/kindnet                kindnet-cni            docker.io/kindest/kindnetd:v20251212-v0.29.0-alpha-10...
  kube-system/DaemonSet/kube-proxy             kube-proxy             registry.k8s.io/kube-proxy:v1.35.0

[Missing startup] (4)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  kube-system/Deployment/coredns               coredns                registry.k8s.io/coredns/coredns:v1.13.1
  local-path-storage/Deployment/local-path-... local-path-provisioner docker.io/kindest/local-path-provisioner:v20251212-v0...
  kube-system/DaemonSet/kindnet                kindnet-cni            docker.io/kindest/kindnetd:v20251212-v0.29.0-alpha-10...
  kube-system/DaemonSet/kube-proxy             kube-proxy             registry.k8s.io/kube-proxy:v1.35.0

[PDB missing] (2)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  kube-system/Deployment/coredns               -                      -
  local-path-storage/Deployment/local-path-... -                      -

[NetworkPolicy missing] (2)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  kube-system/Namespace/kube-system            -                      -
  local-path-storage/Namespace/local-path-s... -                      -

[Secret env usage] (0)
  No issues found

[Manual review] (3)
  Namespace/Kind/Name                          Container             Image
  ------------------------------------------------------------------------------
  -/Manual/PodSecurityStandards                -                      -
  -/Manual/ImageSigningAndVulnScan             -                      -
  -/Manual/RBACLeastPrivilege                  -                      -

Total issues: 40
Advisories: 3 (not counted)

Docker

docker build -t kube-leak-detector:local .
docker run --rm \
  -e KUBECONFIG=/root/.kube/config \
  -v $HOME/.kube:/root/.kube \
  kube-leak-detector:local

Kubernetes CronJob

Deploy the CronJob and RBAC, then configure webhook secrets.

kubectl apply -f k8s/rbac.yaml
kubectl apply -f k8s/cronjob.yaml

The CronJob references a leak-detector-webhooks secret with slack and teams keys. Adjust namespaces and schedules as needed.

Security and permissions

The tool only reads pod metadata. The sample RBAC grants list/watch/get on pods across namespaces.

Troubleshooting

  • Forbidden errors: ensure the service account has cluster-wide read on pods.
  • Empty report: verify kubeconfig context or in-cluster service account.
  • Teams webhook fails: confirm the incoming webhook is enabled and uses the correct URL.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

kube_leak_detector-0.1.0.tar.gz (10.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kube_leak_detector-0.1.0-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file kube_leak_detector-0.1.0.tar.gz.

File metadata

  • Download URL: kube_leak_detector-0.1.0.tar.gz
  • Upload date:
  • Size: 10.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for kube_leak_detector-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a9875d1e3724ead39b861232fb103aa29514fd4464126106774991124572fa82
MD5 e64c6f5998b5bbb2f1adeb25fbec03d5
BLAKE2b-256 0127038cf7669279dd390e7fe7697aeca1c73b68a630cf6071617fe12214b5d5

See more details on using hashes here.

File details

Details for the file kube_leak_detector-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for kube_leak_detector-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8e487bc4217c64e0a48a88f62fbd7437b89ca53dcccb77e31e38363b33eafc56
MD5 9f3ba4e1047d1fb0121598fa245e1951
BLAKE2b-256 8de075033443c72bdbfd82c4340afffec4eba3ba17cfb970cc87a9758b675014

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page