CKA Certification Course - Certified Kubernetes Administrator

Application Lifecycle Management

Secrets

Welcome to this comprehensive guide on managing Secrets in Kubernetes. In this article, we explain how to securely handle sensitive data (such as passwords and keys) in your Kubernetes deployments while avoiding common pitfalls like hardcoding credentials in your application.

Problem with Hardcoding Sensitive Data

Consider a simple Python web application connecting to a MySQL database. When the connection succeeds, the application displays a success message. However, the code includes hardcoded values for hostname, username, and password, which poses a serious security risk.

Previously, configuration data like these values might have been stored in a ConfigMap. For example:

import os
from flask import Flask, render_template
import mysql.connector

app = Flask(__name__)

@app.route("/")
def main():
    mysql.connector.connect(host="mysql", database="mysql",
                              user="root", password="paswrd")
    return render_template('hello.html', color=fetchcolor())

if __name__ == "__main__":
    app.run(host="0.0.0.0", port="8080")
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  # Configuration data goes here

While storing non-sensitive details like hostnames or usernames in a ConfigMap is acceptable, placing a password in such a resource is not secure. Kubernetes Secrets provide a mechanism to safely store sensitive information by encoding the data (note: this is not encryption by default).

Note

Secrets encode data using Base64. Although it provides obfuscation, it is not a substitute for encryption.

Understanding Kubernetes Secrets

Working with Secrets in Kubernetes involves two main steps:

  1. Create the Secret.
  2. Inject it into a Pod.

Below is an illustration of Secret data in their encoded and decoded forms:

Encoded Values

DB_Host: bXlzcWw=
DB_User: cm9vdA==
DB_Password: cGFzd3Jk

Decoded Values

DB Host: mysql
DB User: root
DB Password: paswrd

There are two primary approaches to creating a Secret:

  • Imperative Creation: Using the command line to create Secrets on the fly.
  • Declarative Creation: Defining Secrets in YAML files.

Imperative Creation of a Secret

With the imperative method, you can supply key-value pairs directly via the command line. For example, to create a Secret named "app-secret" with the key-value pair DB_Host=mysql:

kubectl create secret generic app-secret --from-literal=DB_Host=mysql

To include multiple key-value pairs, use the --from-literal option repeatedly:

kubectl create secret generic app-secret \
  --from-literal=DB_Host=mysql \
  --from-literal=DB_User=root \
  --from-literal=DB_Password=paswd

Alternatively, create a Secret from a file with the --from-file option:

kubectl create secret generic app-secret --from-file=app_secret.properties

Declarative Creation of a Secret

For a more manageable approach, define a Secret in a YAML file. This file should include the API version, kind, metadata, and encoded data. Below is a sample YAML definition for a Secret:

apiVersion: v1
kind: Secret
metadata:
  name: app-secret
data:
  DB_Host: bXlzcWw=
  DB_User: cm9vdA==
  DB_Password: cGFzd3Jk

Apply the definition with the following command:

kubectl create -f secret-data.yaml

Converting Plaintext to Base64

On Linux hosts, you can convert plaintext values to Base64-encoded strings using the echo -n command piped to base64. For example:

echo -n 'mysql' | base64
echo -n 'root' | base64
echo -n 'paswrd' | base64
# Output: cGFzd3Jk

Viewing and Decoding Secrets

After creating a Secret, you can list and inspect it with the following commands:

  • List Secrets:

    kubectl get secrets
    

    Expected output:

    NAME          TYPE    DATA   AGE
    app-secret    Opaque    3    10m
    
  • Describe a Secret (without showing sensitive data):

    kubectl describe secret app-secret
    
  • View the encoded data in YAML format:

    kubectl get secret app-secret -o yaml
    

If you need to decode an encoded value, use the base64 --decode command:

echo -n 'bXlzcWw=' | base64 --decode
echo -n 'cm9vdA==' | base64 --decode
echo -n 'cGFzd3Jk' | base64 --decode
# Output: paswrd

Injecting Secrets into a Pod

Once the Secret is created, you can inject it into a Pod using environment variables or by mounting them as files in a volume.

Injecting as Environment Variables

Below is an example Pod definition that injects the Secret as environment variables:

# pod-definition.yaml
apiVersion: v1
kind: Pod
metadata:
  name: simple-webapp-color
  labels:
    name: simple-webapp-color
spec:
  containers:
  - name: simple-webapp-color
    image: simple-webapp-color
    ports:
    - containerPort: 8080
    envFrom:
    - secretRef:
        name: app-secret

Mounting Secrets as Files

Alternatively, mount the Secret as files within a volume. Each key in the Secret becomes a separate file:

volumes:
- name: app-secret-volume
  secret:
    secretName: app-secret

After mounting, listing the directory contents should display each key as a file:

ls /opt/app-secret-volumes
# Output: DB_Host  DB_Password  DB_User

To view the content of a specific file, such as the DB password:

cat /opt/app-secret-volumes/DB_Password
# Output: paswrd

Important Considerations When Using Secrets

Warning

Remember that Kubernetes Secrets are only encoded in Base64, not encrypted by default. Anyone with sufficient access can decode the data. Always handle secret definition files with care and avoid storing them in public repositories.

Here are some key considerations:

  • Secrets offer only Base64 encoding. For enhanced security, consider enabling encryption at rest for etcd.
  • Limit access to Secrets using Role-Based Access Control (RBAC). Restrict permissions to only those who require it.
  • Avoid storing sensitive secret definition files in source control systems that are publicly accessible.
  • For even greater security, explore third-party secret management solutions such as AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, or Vault.

External Secret Providers

External secret providers decouple secret management from etcd and offer advanced encryption, granular access control, and comprehensive auditing capabilities. For further details and best practices, consider exploring courses like the Certified Kubernetes Security Specialist (CKS).

The image provides guidelines on handling secrets, emphasizing encryption, access control, and considering third-party providers for secure storage.

Conclusion

Managing Kubernetes Secrets effectively is crucial for maintaining the security of your applications. By following the best practices outlined above, including using Secrets to handle sensitive data and applying strict RBAC policies, you can mitigate potential security risks associated with managing sensitive configuration data.

Practice these approaches using hands-on labs and ensure your Kubernetes clusters are secure.

For additional resources, consider the following links:

Watch Video

Watch video content

Practice Lab

Practice lab

Previous
Solution Env Variables Optional