Finding Unused Kubernetes Secrets: How Kor Detects Orphaned Secrets
Discover how Kor identifies orphaned Kubernetes Secrets by building a full reference graph across workloads, helping reduce security risks and operational uncertainty.
Finding Unused Kubernetes Secrets: How Kor Detects Orphaned Secrets Across All Usage Patterns
Kubernetes Secrets are everywhere. They back application config, service identity, API tokens, TLS certs, registry credentials—nearly every workload depends on them. But clusters change constantly: apps are redeployed, Helm releases upgraded, namespaces deleted, controllers removed.
What doesn’t change nearly as fast? Secrets.
Over time, clusters quietly accumulate unused Secrets with no clear owner and no obvious way to tell whether they’re still safe to delete. The result is a larger attack surface, harder audits, and growing operational uncertainty.
Kor is an open-source Kubernetes orphaned-resource finder built to solve exactly this problem. It doesn’t rely on labels, naming conventions, or best guesses. Instead, Kor builds a full reference graph across Pods, controllers, ServiceAccounts, Ingresses, and more—so it can reliably determine which Secrets are actually in use and which are truly orphaned.
In this post, we’ll break down every way Secrets can be referenced in Kubernetes, why unused Secrets are risky, and how Kor detects orphaned Secrets even in complex, real-world clusters.
Why Unused Secrets Are a Problem
-
Security exposure
A Secret that’s no longer used still contains sensitive data. The more unused Secrets you have, the larger your attack surface becomes. -
Compliance & auditing complexity
Teams struggle to track whether a Secret is actually required by production workloads. Auditors often ask for validation of live usage. -
Operational confusion
When multiple versions of a Secret exist (e.g.,api-token-v1,api-token-v2,api-token-v3), it’s hard to know what is safe to delete. -
Drift from GitOps
GitOps systems often leave Secrets behind after rollbacks or chart removals.
Kor solves this by deeply analyzing all references to Secrets across Pods, controllers, and workload types.
All the Ways Kubernetes Secrets Can Be Used
Kor’s Secret detection logic needs to understand every reference type. Here are all the mechanisms Kubernetes provides—and how workloads typically use them.
1. Mounted as volume sources
The most common pattern:
yamlvolumes: - name: api-key secret: secretName: payment-api-token
Or mounted via a CSI SecretStore provider.
Kor detects:
secret.secretNamein all Pod templates- References in Deployments, StatefulSets, DaemonSets, ReplicaSets, Jobs, CronJobs
2. Injected as environment variables
Secrets can populate environment variables one-by-one:
yamlenv: - name: API_TOKEN valueFrom: secretKeyRef: name: payment-api-token key: token
Or all keys can be injected at once:
yamlenvFrom: - secretRef: name: payment-api-token
Kor detects:
secretKeyRefsecretRefenvFrom.secretRef- All keys under them
3. Referenced by imagePullSecrets
Used to authenticate to private Docker registries:
yamlimagePullSecrets: - name: registry-login
Kor detects:
- Secret usage in all Pod specs and ServiceAccounts’
imagePullSecrets
4. Used by ServiceAccounts
Secrets can be explicitly linked:
yamlsecrets: - name: my-service-account-token
This is especially relevant for:
- Manually created tokens
- Legacy (non-projected) ServiceAccount token Secrets
Kor detects:
- Explicit
sa.secrets[*].name - Auto-generated tokens associated with ServiceAccounts
- Whether a ServiceAccount is actually used by running Pods (Kor’s unused SA logic)
5. TLS Secrets used by Ingress resources
TLS certs are stored as Secrets:
yamltls: - hosts: - example.com secretName: example-tls
Kor detects:
tls.secretNamein Ingress definitions- Whether the Ingress actually exists and routes traffic
6. CSI Secret providers (external secrets)
Some clusters use Secret Store CSI:
yamlvolumes: - name: vault-secrets csi: driver: secrets-store.csi.k8s.io volumeAttributes: secretProviderClass: vault-provider
This may result in ephemeral Secrets being synced to Kubernetes.
Kor detects:
- K8s-level Secrets produced by CSI sync (static Secrets)
- If those Secrets are mounted or referenced by Pods
- (Kor does not inspect provider-specific external secrets outside Kubernetes, staying OSS-friendly.)
7. Webhook or controller certificates
Some controllers create their own TLS Secrets:
- Admission webhook certs
- Operator self-signed certs
- Mutating/validating webhook configurations referencing Secrets
Example:
yamlwebhooks: clientConfig: caBundle: ... service: name: webhook-service
Or:
yamlclientConfig: url: https://webhook.svc caBundle: ...
Kor’s scope:
- Detect Secrets referenced explicitly in webhook configurations
- Detect unused Secrets in the namespace when controllers are removed
8. Helm-managed Secrets
Helm creates Secrets for release metadata, e.g.:
textsh.helm.release.v1.my-app.v3
These can remain after:
- Failed installs
- Partial upgrades
- Namespace deletions
- Manual cleanup
Kor note: Helm release Secrets (helm.sh/release.v1) are ignored by default to reduce noise, since they are Helm metadata rather than application secrets.
How Kor Detects Orphaned Secrets Across All These Patterns
Kor builds a resource reference graph:
- Collect Secrets across all namespaces.
- Collect workload specs (Pods and controllers) and scan for all reference types.
- Collect ServiceAccounts & image pull Secret associations.
- Collect Ingress TLS references.
- Consider controller-generated Secrets (except excluded types).
- Mark a Secret as “used” if any reference is found.
- All remaining Secrets are reported as unused.
Secret types Kor ignores by default
Kor does not flag the following Secret types as unused to avoid noisy or low-value findings:
helm.sh/release.v1(Helm metadata)kubernetes.io/dockerconfigjsonkubernetes.io/dockercfgkubernetes.io/service-account-token
This approach ensures Kor catches the edge cases other scanners miss.
Example Kor output
yamlunusedSecrets: - namespace: default name: payment-api-token-old - namespace: staging name: registry-cred-backup - namespace: team-a name: sh.helm.release.v1.service.v5 # stale release
Every reported Secret is one that has no live consumer anywhere in the cluster.
Using Kor to Scan for Unused Secrets
Run:
bashkor secrets
Or full scan:
bashkor all
You can also target a namespace:
bashkor all --namespace team-a
Results are exported in YAML or JSON for automation.
Conclusion
Secrets are referenced in many different ways throughout Kubernetes—volumes, env vars, ServiceAccounts, Ingresses, image pull creds, and more. This makes detecting unused Secrets difficult to do manually or with simple scripts.
Kor solves this cleanly, by performing a full graph-based analysis and identifying Secrets that are truly unused anywhere in the cluster.
Cleaning up these Secrets reduces risk, simplifies audits, and improves operational clarity.
Related Articles
Why GitOps Doesn't Mean Clean: The Blind Spot in Your Cluster Strategy
GitOps handles creation perfectly but fails at deletion. Discover how KorPro identifies the 'Shadow Cluster' of orphaned resources, unconnected Helm configs, and human-created leftovers.
How to Calculate Kubernetes Cost Savings
Learn how to accurately calculate potential cost savings from Kubernetes resource optimization and make data-driven decisions.
Kubernetes Security: Identifying Orphaned Secrets and ConfigMaps
Learn how orphaned Secrets and ConfigMaps pose security risks and how to identify and remediate them effectively.
Written by
Yohah Dissen