Back to Blog
Security

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.

Yohah Dissen
December 29, 2025
5 min read
KubernetesSecretsKorSecurityOrphaned Resources

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

  1. 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.

  2. Compliance & auditing complexity
    Teams struggle to track whether a Secret is actually required by production workloads. Auditors often ask for validation of live usage.

  3. 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.

  4. 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:

yaml
volumes: - name: api-key secret: secretName: payment-api-token

Or mounted via a CSI SecretStore provider.

Kor detects:

  • secret.secretName in all Pod templates
  • References in Deployments, StatefulSets, DaemonSets, ReplicaSets, Jobs, CronJobs

2. Injected as environment variables

Secrets can populate environment variables one-by-one:

yaml
env: - name: API_TOKEN valueFrom: secretKeyRef: name: payment-api-token key: token

Or all keys can be injected at once:

yaml
envFrom: - secretRef: name: payment-api-token

Kor detects:

  • secretKeyRef
  • secretRef
  • envFrom.secretRef
  • All keys under them

3. Referenced by imagePullSecrets

Used to authenticate to private Docker registries:

yaml
imagePullSecrets: - name: registry-login

Kor detects:

  • Secret usage in all Pod specs and ServiceAccounts’ imagePullSecrets

4. Used by ServiceAccounts

Secrets can be explicitly linked:

yaml
secrets: - 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:

yaml
tls: - hosts: - example.com secretName: example-tls

Kor detects:

  • tls.secretName in Ingress definitions
  • Whether the Ingress actually exists and routes traffic

6. CSI Secret providers (external secrets)

Some clusters use Secret Store CSI:

yaml
volumes: - 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:

yaml
webhooks: clientConfig: caBundle: ... service: name: webhook-service

Or:

yaml
clientConfig: 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.:

text
sh.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:

  1. Collect Secrets across all namespaces.
  2. Collect workload specs (Pods and controllers) and scan for all reference types.
  3. Collect ServiceAccounts & image pull Secret associations.
  4. Collect Ingress TLS references.
  5. Consider controller-generated Secrets (except excluded types).
  6. Mark a Secret as “used” if any reference is found.
  7. 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/dockerconfigjson
  • kubernetes.io/dockercfg
  • kubernetes.io/service-account-token

This approach ensures Kor catches the edge cases other scanners miss.

Example Kor output

yaml
unusedSecrets: - 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:

bash
kor secrets

Or full scan:

bash
kor all

You can also target a namespace:

bash
kor 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.

Written by

Yohah Dissen

View All Posts