1. Overview

Kubernetes is a widely adopted open-source orchestration engine that enables the management and deployment of containerized applications. Further, a pivotal aspect of Kubernetes is its ability to securely store confidential information by utilizing secrets, including passwords, API keys, and certificates.

When generating a Kubernetes secret, the data is encoded in Base64 format to ensure the integrity of secrets in situations without Unicode or arbitrary character support.

In this tutorial, we’ll elucidate the definition of Kubernetes secrets, their functionalities, and the process of decoding them to access their contents in a human-readable format.

2. Kubernetes Secrets

Kubernetes secrets are objects used to preserve and manage sensitive data. In particular, they often store information like passwords, API keys, and certificates, needed by applications to connect to external services and resources. On the other hand, secrets themselves are stored as objects in the Kubernetes API server and are associated with a specific namespace.

2.1. Creating a Kubernetes Secret

Usually, we generate Kubernetes secrets using the kubectl command-line tool or the Kubernetes API. When creating a secret, the data is encoded in the Base64 format to optimize the storage and transfer.

So, let’s see an example of how to create a secret:

$ kubectl create secret generic my-secret --from-literal=username=admin --from-literal=password=secret
secret/my-secret created

In this case, we create a secret named my-secret with two key-value pairs: username=admin and password=secret.

2.2. Viewing the Encoded Data

To view the encoded data of a Kubernetes secret, we can use the get secret subcommand:

$ kubectl get secret my-secret -o jsonpath='{.data}'
{"password":"c2VjcmV0","username":"YWRtaW4="}

Here, the output is in the JSON format, but each value is further encoded in Base64.

2.3. Decoding Base64 Data

To decode the Base64-encoded data of a Kubernetes secret, we can use the base64 command:

$ echo "<encoded-string>" | base64 --decode

Notably, we replace with the encoded parts of the JSON object we saw earlier:

$ echo "YWRtaW4=" | base64 --decode
admin

To be clear, if we were to pass the whole JSON object as is, the output would be invalid.

2.4. Decoding Kubernetes Secret

Now that we know how to decode Base64 data, let’s see how to view the contents of a Kubernetes secret:

$ kubectl get secret my-secret -o jsonpath='{.data.password}' | base64 --decode
secret

Thus, we use the jsonpath parameter to only extract the value of the data.password field and then pipe the result to the base64 command. Thus, we get the decoded data as the output.

3. Show Kubernetes Secrets at Different Levels

For convenience, we may sometimes need to see multiple secrets at different levels from a given Kubernetes configuration. However, printing them one by one can be inconvenient.

Still, let’s begin with the basic case.

3.1. Show and Decode Specific Secret

In general, as we saw earlier, we can output a secret via the get secret subcommand:

$ kubectl get secret my-secret --namespace default -o json
{
    "apiVersion": "v1",
    "data": {
        "password": "c2VjcmV0",
        "username": "YWRtaW4="
    },
    "kind": "Secret",
    "metadata": {
        "creationTimestamp": "2024-08-29T06:18:18Z",
        "name": "my-secret",
        "namespace": "default",
        "resourceVersion": "4878573",
        "uid": "3f7f26bd-7f64-4759-a04c-a2f5ab3f880d"
    },
    "type": "Opaque"
}

In this case, we specify the secret name and its namespace.

To select only the data and exclude the metadata without changing the original kubectl command, we can employ jq:

$ kubectl get secret my-secret --namespace default -o json | jq '{name: .metadata.name,data: .data|map_values(@base64d)}'
{
  "name": "my-secret",
  "data": {
    "password": "secret",
    "username": "admin"
  }
}

Thus, the path selection happens through jq:

  • select the .metadata.name field as name
  • select all values in the .data field and decode them via the built-in base64d method as data

This way, we can expand the command to show and decode all secrets of the default namespace at once.

3.2. Show and Decode Namespace Secrets

To expand on the last method, we use jq it to show the decoded version of all secrets of a given namespace:

$ kubectl get secrets --namespace demo -o json | jq '.items[] | {name: .metadata.name,data: .data|map_values(@base64d)}'
{
  "name": "secret-separate-spec",
  "data": {
    "key": "K222F",
    "user": "demo"
  }
}

In this case, the get secrets subcommand extracts all secrets from the demo namespace. If we don’t explicitly specify a namespace, Kubernetes uses the default one.

3.3. Show and Decode All Secrets

Naturally, we can iterate through all namespaces which have secrets of interest. However, the get secrets subcommand also offers an –all-namespaces flag:

$ kubectl get secrets --all-namespaces -o json | jq '.items[] | {name: .metadata.name,data: .data|map_values(@base64d)}'
{
  "name": "my-secret",
  "data": {
    "password": "secret",
    "username": "admin"
  }
}
{
  "name": "secret-spec",
  "data": {
    "key": "F666A"
  }
}
{
  "name": "secret-separate-spec",
  "data": {
    "key": "K222F",
    "user": "demo"
  }
}

Thus, we see the decoded version of all secrets from all namespaces in the same format.

4. Best Practices for Managing Secrets

When managing secrets in Kubernetes, it’s important to follow best practices to ensure the security of applications. Let’s see some best practices to keep in mind.

4.1. Use Separate Secrets for Each Sensitive Data Item

One of the best practices for managing secrets in Kubernetes is to use separate secrets for each sensitive data item. Thus, we help limit the impact of a security breach. For example, if an attacker gains access to a secret containing a password, they would only have access to the resources linked with that specific password rather than all available data.

4.2. Use Strong Passwords and Keys

Another best practice for managing secrets in Kubernetes is to use strong passwords and keys. This helps prevent brute-force attacks, where an attacker tries to guess the password or key using automated tools. We can use tools like pwgen to generate strong passwords and openssl to generate strong keys.

4.3. Restrict Access to Secrets

It’s important to restrict access to secrets to only the applications and services that need them. This helps to prevent unauthorized access to sensitive data. In Kubernetes, we can use RBAC (Role-Based Access Control) to restrict secret access.

4.4. Rotate Secrets Regularly

Yet another way to protect secrets is to rotate their contents regularly. This helps to prevent attacks that rely on the fact that the secret has been in use for a long time. We can use tools like kubctl-secrets-controller to automate the rotation of secrets.

4.5. Use Encryption at Rest and in Transit

It’s important to use encryption to protect secrets at rest (when stored) and in transit (when transmitted between services). In Kubernetes, we can use TLS (Transport Layer Security) to encrypt communication between services and encryption providers like sops or vault to encrypt secrets at rest.

5. Conclusion

In this article, we covered the fundamentals of Kubernetes secrets with details about their encoding and decoding processes.

Further, we included some best practices for proper Kubernetes secret management. In conclusion, following these guidelines can be essential to ensure the security of sensitive data in applications and prevent unauthorized access.