Kubernetes and Cloud Native Security Associate (KCSA)

Kubernetes Security Fundamentals

Secrets

In this lesson, we’ll refactor a simple Python web application that connects to a MySQL database. Currently, the database hostname, username, and password are hardcoded in the source—an insecure practice. We'll move sensitive values into Kubernetes Secrets to keep credentials safe.

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

app = Flask(__name__)

@app.route("/")
def main():
    conn = mysql.connector.connect(
        host=os.environ["DB_HOST"],
        database="mysql",
        user=os.environ["DB_USER"],
        password=os.environ["DB_PASSWORD"]
    )
    return render_template("hello.html", color=fetchcolor())

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

Warning

Never store plaintext passwords in your code or in a ConfigMap. Use Kubernetes Secrets for all sensitive data.

ResourcePurposeExample
ConfigMapNon-sensitive configuration dataDB_HOST, DB_USER
SecretSensitive data (passwords, keys)DB_PASSWORD, API tokens, TLS certificates

1. Creating Secrets

Secrets can be created imperatively with kubectl or declaratively with a YAML manifest.

1.1 Imperative Creation

Specify key-value pairs on the command line:

kubectl create secret generic app-secret \
  --from-literal=DB_HOST=mysql \
  --from-literal=DB_USER=root \
  --from-literal=DB_PASSWORD=paswrd

Or load from a file (key=value per line):

# app_secret.properties
DB_HOST=mysql
DB_USER=root
DB_PASSWORD=paswrd

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

1.2 Declarative Creation

Define a Secret manifest in secret-data.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque
data:
  DB_HOST: bXlzcWw=      # echo -n 'mysql' | base64
  DB_USER: cm9vdA==      # echo -n 'root'  | base64
  DB_PASSWORD: cGFzd3Jk  # echo -n 'paswrd'| base64

Apply the manifest:

kubectl apply -f secret-data.yaml

Generate base64-encoded values:

echo -n 'mysql'  | base64  # bXlzcWw=
echo -n 'root'   | base64  # cm9vdA==
echo -n 'paswrd' | base64  # cGFzd3Jk

Note

You can decode any value with echo '<base64>' | base64 --decode. Keep your raw files out of version control.


2. Viewing Secrets

List all existing secrets:

kubectl get secrets

Inspect a specific secret:

kubectl describe secret app-secret

Output the raw YAML (with encoded data):

kubectl get secret app-secret -o yaml

Decode an encoded secret value:

echo 'cGFzd3Jk' | base64 --decode  # outputs: paswrd

3. Injecting Secrets into Pods

You can consume Secrets as environment variables or as mounted volumes.

3.1 All Keys as Environment Variables

Add an envFrom directive to your Pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: simple-webapp
spec:
  containers:
    - name: simple-webapp
      image: simple-webapp:latest
      ports:
        - containerPort: 8080
      envFrom:
        - secretRef:
            name: app-secret

Apply the Pod definition:

kubectl apply -f pod-definition.yaml

3.2 Single Key as an Environment Variable

Use valueFrom.secretKeyRef for a specific key:

env:
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: app-secret
        key: DB_PASSWORD

3.3 Mounting Secrets as Files

Mount the secret into a volume:

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

containers:
  - name: simple-webapp
    image: simple-webapp:latest
    volumeMounts:
      - name: app-secret-volume
        mountPath: /opt/secrets

Inside the running Pod:

ls /opt/secrets
cat /opt/secrets/DB_PASSWORD
# paswrd

Try the exercises to practice creating, viewing, and injecting Secrets in your Kubernetes clusters!

Watch Video

Watch video content

Practice Lab

Practice lab

Previous
Solution RBAC