Kubernetes Secrets Explained: Securely Injecting Sensitive Data into Pods

0

 

When you build applications on Kubernetes, you quickly run into a common problem: your workloads need credentials, API keys, tokens, or certificates, but you do not want to hardcode them into your images or application code.

That is where Secrets come in.

Kubernetes Secrets let you store sensitive values separately from your application and inject them into Pods only when needed. They work similarly to ConfigMaps from the Pod’s perspective, but they are meant for confidential data.

In this article, we will look at what Secrets are, how they differ from ConfigMaps, how to create them, and how to consume them through environment variables or mounted files. We will also cover the security caveats that every developer and cluster operator should know.


What Is a Kubernetes Secret?

A Secret is a Kubernetes object designed to hold a small amount of sensitive data, such as:

  • database usernames and passwords
  • API tokens
  • TLS certificates and keys
  • registry credentials

The main benefit is decoupling sensitive configuration from your application code and container image. Instead of baking credentials into an image or a deployment manifest, you store them separately and reference them from the Pod.

This improves portability and reduces the chance of accidental exposure during normal development and deployment workflows.


Secrets Are Not Encryption by Themselves

One of the most important things to understand is this:

Base64 is encoding, not encryption.

If you look at a Secret manifest, you will often see values under the data field that look unreadable. That does not mean the secret is secure by default. Base64 can be decoded very easily.

For example:

apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  username: ZGJfdXNlcgo=
  password: ZGJfcGFzcwo=

Those values are just base64-encoded strings.

Anyone who can retrieve the Secret data can decode it:

echo 'ZGJfdXNlcgo=' | base64 --decode

That is why you should never treat base64 as a protection mechanism.


Why Secrets Still Matter

If Secrets are not encrypted by default, why use them at all?

Because they still give you an important separation of concerns:

  • your app code stays free of hardcoded credentials
  • the Secret can be managed independently from the Pod
  • access can be controlled with RBAC
  • managed Kubernetes platforms often integrate with external secret managers
  • Kubernetes provides standard ways to inject credentials into workloads

So Secrets are useful, but they are only part of the security story.


Common Secret Types

The most common Secret type is:

  • Opaque — a generic key-value secret

You will also encounter built-in types for more specific use cases, such as:

  • TLS secrets
  • Docker or registry authentication secrets

If you see type: Opaque, it usually means the Secret contains arbitrary key-value pairs.


Creating a Secret with kubectl

In practice, many teams prefer creating Secrets with kubectl instead of writing raw secret values into YAML files.

A simple example:

kubectl create secret generic db-creds \
  --from-literal=username=db_user \
  --from-literal=password=db_pass

This creates a generic Secret named db-creds.

To list Secrets:

kubectl get secrets

To inspect the object:

kubectl describe secret db-creds

Notice that kubectl describe does not print the actual values. It only shows metadata and the size of each entry.

If you explicitly output the full object, you will still see the base64 data:

kubectl get secret db-creds -o yaml

And if you want to inspect one value:

kubectl get secret db-creds -o jsonpath='{.data.username}' | base64 --decode
echo

That is exactly why storing Secrets in Git as plain manifests is risky. A base64-encoded value is still effectively plain text for anyone who can access the file.


Using a Secret as Environment Variables

One common pattern is mapping specific Secret keys into environment variables.

Here is a Pod example:

apiVersion: v1
kind: Pod
metadata:
  name: secret-env-demo
spec:
  containers:
    - name: my-app
      image: busybox:1.36.1
      command: ["sh", "-c", "echo \"$DB_USER\" && echo \"$DB_PASSWORD\" && sleep 1800"]
      env:
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: db-creds
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-creds
              key: password

Apply it:

kubectl apply -f pod.yaml

Check the logs:

kubectl logs secret-env-demo

The container receives the decoded values, not the base64 strings.

That is convenient, but it also means your application must handle secrets carefully. If your app prints environment variables or logs credentials, the Secret is exposed in plain text at runtime.

Importing All Keys at Once

If you want to expose all keys from a Secret as environment variables, you can use envFrom:

envFrom:
  - secretRef:
      name: db-creds

That is shorter, but it also makes it easier to expose more data than you really need. In most production setups, mapping only the required keys is the safer option.

Important Runtime Note

If a container consumes Secret data through environment variables, updates to the Secret are not reflected in the running container automatically. The container typically needs a restart to pick up the new values.


Mounting a Secret as Files

Another common pattern is mounting Secret data as files.

This is especially useful when an application expects:

  • a certificate file
  • a private key
  • a config file
  • credentials stored at a filesystem path

Example:

apiVersion: v1
kind: Pod
metadata:
  name: secret-volume-demo
spec:
  containers:
    - name: my-app
      image: busybox:1.36.1
      command: ["sh", "-c", "sleep 1800"]
      volumeMounts:
        - name: db-secrets
          mountPath: /etc/db
          readOnly: true
  volumes:
    - name: db-secrets
      secret:
        secretName: db-creds

After the Pod starts, each key becomes a file under /etc/db:

kubectl exec -it secret-volume-demo -- sh
cd /etc/db
ls
cat username
cat password

If your Secret contains username and password, Kubernetes will create files with those names.

Mounting Only Selected Keys

You can also mount only selected keys and control the output path:

volumes:
  - name: db-secrets
    secret:
      secretName: db-creds
      items:
        - key: password
          path: dev/password

With that configuration, only the password key is mounted, and it appears at:

/etc/db/dev/password

This is useful when:

  • your application expects a specific file layout
  • you want to avoid exposing unnecessary keys
  • you want cleaner separation between secret values

Secret Security Best Practices

Using Secrets correctly is just as important as creating them.

Here are the practices that matter most:

  • Enable encryption at rest for Secrets in your cluster.
  • Use least-privilege RBAC so only the right identities can read or modify Secrets.
  • Avoid storing Secret manifests with sensitive data in source control.
  • Do not log secret values from your application.
  • Only expose a Secret to the containers that actually need it.
  • Be careful with kubectl exec: anyone who can exec into a Pod that mounts a Secret may be able to read it.
  • Consider using a cloud secret manager or an external secret solution for stronger operational security.

A subtle but important point: in Kubernetes, a user who is allowed to create a Pod in a namespace can potentially gain access to Secrets in that namespace by mounting or referencing them. That is why namespace-level permissions must be designed carefully.


Secrets vs ConfigMaps

A simple rule of thumb:

  • use ConfigMaps for non-sensitive configuration
  • use Secrets for sensitive configuration

From a Pod perspective, both can be consumed in similar ways:

  • environment variables
  • mounted files

But the intent is different, and the security handling should be different too.

If a value would be a problem when exposed publicly, it belongs in a Secret, not in a ConfigMap.


Managed Kubernetes and External Secret Managers

When you use a managed Kubernetes platform, you often get better options than storing every credential directly in native Secret objects.

Common approaches include integrating Kubernetes with:

  • cloud-native secret managers
  • external secret operators
  • Vault-style centralized secret systems

This can improve rotation, auditability, and lifecycle management.

Native Kubernetes Secrets are still widely used, but many teams treat them as the delivery mechanism rather than the ultimate source of truth.


Final Thoughts

Kubernetes Secrets solve a real operational need: they let you keep sensitive values outside your application code and inject them into Pods in a standard way.

But they are not magically secure by default.

Base64 is not encryption. A Secret becomes plain text again when your container reads it. And weak RBAC or careless logging can undo the benefit very quickly.

So the right mindset is this:

  • use Secrets instead of hardcoding credentials
  • understand their limits
  • apply RBAC carefully
  • enable encryption at rest
  • protect the runtime path just as much as the stored value

If you do that, Secrets become a practical and useful part of your Kubernetes security model instead of a false sense of safety.


References

  • Kubernetes documentation: Secrets - kubernetes[.]io/docs/concepts/configuration/secret/
  • Kubernetes documentation: Distribute Credentials Securely Using Secrets - kubernetes[.]io/docs/tasks/inject-data-application/distribute-credentials-secure/
  • Kubernetes documentation: Good practices for Kubernetes Secrets - kubernetes[.]io/docs/concepts/security/secrets-good-practices/
  • Kubernetes documentation: kubectl create secret generic - kubernetes[.]io/docs/reference/kubectl/generated/kubectl_create/kubectl_create_secret_generic/

Post a Comment

0 Comments

Post a Comment (0)

#buttons=(Ok, Go it!) #days=(20)

This site uses cookies from Google to deliver its services and analyze traffic. Your IP address and user-agent are shared with Google along with performance and security metrics to ensure quality of service, generate usage statistics, and to detect and address abuse. More Info
Ok, Go it!