Quick Links

Kubernetes Secrets let you store confidential information safely. Using a Secret removes the need to bake sensitive data into manifest definitions or plain container images.

Secrets are a first-class resource type which exist independently of any Pod. You provide Pods with references to your Secrets. The architecture lets you confine secrets access to only those Pods that actually need the data.

You generally use Secrets for any sensitive constants which your Pods may need. They're ideal for storing authentication keys, database credentials and API tokens.

Creating a Secret

Secrets are created in the same way as any other Kubernetes API resource. You can use Kubectl commands or a YAML manifest which you then apply to your cluster. We'll provide example YAML files in this tutorial.

Heres's how to define a user-created secret:

apiVersion: v1

kind: Secret

type: Opaque

metadata:

name: demo-secret

data:

SECRET_USERNAME: dXNlcm5hbWUK

SECRET_PASSWORD: cGFzc3dvcmQK

Secrets consist of a kind and a simple object of data. The example secret defines two separate data fields, SECRET_USERNAME and SECRET_PASSWORD. Values must be Base64-encoded - the values shown above were originally username and password.

If you're working with a Helm template, you can define your secret values in a values.yaml file. Pipe them through b64enc in your manifest to have Helm encode them as Base64.

apiVersion: v1

kind: Secret

type: Opaque

metadata:

name: demo-secret

data:

SECRET_PASSWORD: {{ .Values.SecretPassword | b64enc }}

If you'd rather not Base64-encode your values, you can use the stringData field instead. Like data, stringData is a map of key-value pairs but the values will be processed verbatim, without any encoding.

Secret Types

The Opaque secret type should be used for arbitrary data that you define yourself. Kubernetes defines a few other built-in secret types intended for specific usage scenarios.

Available types include service-account-token (a Kubernetes service token), dockerconfigjson (a serialised Docker config.json file, to provide Docker credentials) and ssh-auth (provide SSH credentials). In addition to these types, there are solutions for HTTP Basic Authentiation and TLS certificate data.

Each secret type is able to define its own extra fields and validation constraints. You'll typically need to set additional annotations on your secret to provide the data needed by the secret type.

You can create your own secret type identifiers by supplying your own string to the type field. The resulting secret will be functionally equivalent to the Opaque type.

Providing Secrets to Pods

Once you've created a Secret, you need to make it available to your Pods. You can inject secret data as environment variables or as a file mounted into a volume.

Here's a Pod manifest that pulls a secret's data into environment variables:

apiVersion: v1

kind: Pod

metadata:

name: pod-with-secret

spec:

containers:

- name: demo-container

image: my-image:latest

envFrom:

- secretRef:

name: demo-secret

By using envFrom, all the key-value pairs defined in the secret's data will be converted to container environment variables. With the example secret from earlier, your container would have SECRET_USERNAME and SECRET_PASSWORD environment variables injected. Values will be automatically Base64-decoded.

Sometimes you'll want to work with files instead of environment variables. Here's how to mount a secret into a Kubernetes volume.

apiVersion: v1

kind: Pod

metadata:

name: pod-with-secret

spec:

containers:

- name: demo-container

image: my-image:latest

volumeMounts:

- name: secret-volume

mountPath: /secrets

volumes:

- name: secret-volume

secret:

secretName: demo-secret

Access the /secrets directory within the container to view the secret data. Each data key will have its own file. The contents of the file will be the Base64-decoded value of that key. Our example secret would write /secrets/SECRET_USERNAME and /secrets/SECRET_PASSWORD files.

The approach works by creating a Kubernetes volume using the secret source. This source populates the volume with the data from a named secret. The volume then gets mounted into the container at the path defined under volumeMounts.

Security Considerations

Putting data into a secret does not automatically make it secure. The Base64-encoding provides superficial obscurity but make no mistake: this is encoding and not encryption. Anything with access to your cluster, whether a human user or a suitably permissioned application, can retrieve plain-text secret values.

The point of secrets is to reduce the risk of accidental data exposure when creating and viewing Pods. Secrets are only provided to Pods - and to Nodes - which actually require them. Node will destroy their local copies of secrets when the Pod that used them terminates.

The Kubernetes control plane stores secret values within its etcd instance. This is a key-value store which backs the Kubernetes cluster data. If you want to maximise security, you should configure encryption at rest for your etcd store. This will encrypt your secrets within etcd.

You should also assess how your application layer handles secrets. Even with encryption at rest enabled, your containers could inadvertently leak secrets by emitting them to job logs or sending them to external services. Using Kubernetes secrets doesn't obviate the need to carefully handle sensitive data within your container.

Summary

Kubernetes secrets let you store and access confidential data within your Kubernetes cluster. Using secrets gives you more control over information exposure. It also makes it explicit that you're handling potentially sensitive values. This can act as a warning to cluster users and administrators.

Pods consume secrets as either environment variables or volume-mounted files. Kubernetes handles the injection of secrets into containers. Your workloads can access the data using their existing configuration mechanisms. Secrets aren't fully secure by default but you can increase their protection by activating cluster-level encryption.