Security overview
What ClusterNap can touch, and what it can't
Written for the security engineer reviewing us. Every permission the agent holds is on this page, with the reason it needs it.
How it works
One agent pod runs inside your cluster: every 5 minutes it reads each opted-in namespace's schedule and scales its Deployments and StatefulSets to zero or back, and every 15 seconds it checks for wake-up requests. A weekly report job sends the savings summary and, if you enable it, scans AWS for unused resources.
Both connect out to the ClusterNap API over HTTPS to send status and fetch wake-up requests. No inbound connection to your cluster or network is ever needed, and the agent keeps working if the API is unreachable.
Kubernetes permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
rules:
- apiGroups: [""]
resources: [namespaces]
verbs: [get, list, update]
- apiGroups: [apps]
resources: [deployments, statefulsets]
verbs: [get, list, update]
- apiGroups: [""]
resources: [nodes]
verbs: [list]| Permission | Why |
|---|---|
| namespaces: get, list | Find namespaces that carry a cost-optimizer.io/schedule annotation. |
| namespaces: update | Record sleep state and wake-until time in cost-optimizer.io/* annotations. |
| deployments, statefulsets: get, list | Read replica counts and the CPU/memory requests used to price savings. |
| deployments, statefulsets: update | Change spec.replicas, and store the original count in an annotation on the workload. |
| nodes: list | Read-only. Tell whether freed capacity is removed (Karpenter, Cluster Autoscaler, EKS Auto Mode, Fargate) so savings are shown honestly. |
| ingresses: get, list, update (only with wake on visit) | Point a sleeping namespace's Ingresses at the waking-up page and restore them exactly on wake. |
| services: get, create, delete (only with wake on visit) | Create and remove one ExternalName Service, clusternap-waker, in each sleeping namespace. |
Unless you turn on wake on visit, the agent cannot create or delete anything, cannot read Secrets, ConfigMaps or pod logs, and cannot exec into pods. It only acts on namespaces you annotate; a workload annotated cost-optimizer.io/exclude: "true" is never scaled.
AWS permissions (optional)
Only needed for the unused-resource scan. Grant it to the agent's service account with IAM Roles for Service Accounts (IRSA), so no long-lived keys exist:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["ec2:DescribeVolumes", "ec2:DescribeAddresses"],
"Resource": "*"
}]
}Both actions are read-only. ClusterNap never deletes, detaches or modifies AWS resources; it lists them so you can decide.
Data that leaves your cluster
| Sent to the API | Example |
|---|---|
| Namespace name, schedule, time zone | feature-checkout, sun-fri 20:00-08:00, Asia/Tbilisi |
| Sleep state and timestamps | asleep since 2026-10-13 20:00 UTC |
| Savings per finished sleep | 12 hours, $4.31 |
| Unused AWS resources | vol-0a1b2c3d, 500 GiB gp2, $50/month |
Never sent: pod specs, container images, environment variables, Secrets, ConfigMaps, logs or metrics. The Slack webhook URL stays in a Secret in your cluster; reports go straight from your cluster to Slack.
Credentials and storage
- Each cluster gets its own API token. We store only its SHA-256 hash, so a database leak does not expose usable tokens. Deleting the cluster in the dashboard revokes the token immediately.
- Dashboard passwords are hashed with bcrypt. Sessions use HttpOnly, SameSite cookies, stored hashed and expiring after 14 days.
- Every dashboard query is scoped to your organization; tests check that one organization cannot read or change another's data.
- Three wrong passwords for an account lock sign-in for that account from that address for 30 minutes; twenty from anywhere lock the account for 30 minutes. Unknown emails are treated the same, so sign-in does not reveal who has an account.
Agent hardening
- Distroless, static image with no shell or package manager.
- Runs as a non-root user with a read-only root filesystem, all Linux capabilities dropped and the RuntimeDefault seccomp profile.
- Nothing listens on a port: the agent only makes outbound calls.
- The agent sends its token only over HTTPS and refuses a plain-http API address, except on the local machine for testing.
- Wake-up requests from the dashboard are honoured only for namespaces you annotated. Even a compromised ClusterNap API could not make the agent touch any other namespace.
Reporting a vulnerability
Email mikheiljibuti@yahoo.com. We reply within two business days and credit researchers who want to be credited.