Secrets that bundle more than 30 credentials into one object
What does ZopNight detect here?
ZopNight raises a governance finding for any Kubernetes Secret on EKS, GKE or AKS holding more than 30 keys. Kubernetes authorizes access per Secret object, never per key, so one crowded Secret hands every credential inside it to whoever can read it or mount it, and a single leak exposes all of them.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1753 · RC-1853 · RC-1953 |
| Category | governance |
| Severity | low |
| Metric | key count |
| Threshold | more than 30 keys |
| Source | ZopNight |
| Permissions used | list secrets |
Where it applies
Why credential count per Secret matters
Kubernetes RBAC can narrow access down to individual objects through resourceNames, as the
RBAC reference shows, but no
further. There is no rule that grants one key of a Secret and withholds another. A role that
can get a Secret with 40 keys can read all 40.
Mounting behaves the same way by default. The
distribute credentials task
explains that each key in the Secret’s data becomes a file in the mounted directory unless the
volume lists specific items. A pod that needs one database password therefore receives the
payment API key and the SMTP password too. The
Secrets documentation also caps
individual Secrets at 1 MiB, which a bundle of certificates can approach.
This is a governance and security posture finding. Secrets have no cloud charge of their own, so there is no savings figure.
Listing crowded Secrets
The command below needs list on Secrets, which returns the encoded values as well as the
names, so run it with an identity that is already trusted with that data:
kubectl get secrets -A -o json | jq -r ' .items[] | ((.data // {}) | length) as $n | select($n > 30) | "\(.metadata.namespace)/\(.metadata.name) type=\(.type) keys=\($n)"'kubectl get secrets -A without -o json prints a DATA column with the same count if you
prefer not to pull values at all.
When the finding appears
ZopNight reads the number of keys it recorded for each Secret and raises the finding once that number passes 30. The threshold is stricter than the 50 used for ConfigMaps because the contents are credentials. The check is a point-in-time read with no lookback window, and it clears as soon as the Secret is back to 30 keys or fewer.
Secrets it leaves alone
A Secret without a recorded key count is skipped. The rule does not inspect Secret types, so TLS, Docker registry and Opaque Secrets are all judged by the same count. Legacy service-account token Secrets are handled by a separate check, Legacy service-account-token Secret.
Splitting credentials by consumer
- For each key, find the workloads that reference it through
secretKeyRef,envFromor a volume mount. - Create one Secret per consumer, or per trust boundary, holding only the keys that consumer uses.
- Update each workload to reference its own Secret, or, as a stopgap, list only the needed keys
under
.spec.volumes[].secret.items. - Tighten RBAC with
resourceNamesso each service account can read only its own Secret. - Rotate any credential that was broadly readable before the split, then delete the old bundle.